1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
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.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
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.
24 *
25 * Contributor(s):
26 *
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.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS parser.
43 *
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.
51 *
52 * This parser attempts no error recovery.
53 */
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 "jsconfig.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jslock.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsopcode.h"
73 #include "jsparse.h"
74 #include "jsscan.h"
75 #include "jsscope.h"
76 #include "jsscript.h"
77 #include "jsstr.h"
78
79 #if JS_HAS_XML_SUPPORT
80 #include "jsxml.h"
81 #endif
82
83 #if JS_HAS_DESTRUCTURING
84 #include "jsdhash.h"
85 #endif
86
87 /*
88 * JS parsers, from lowest to highest precedence.
89 *
90 * Each parser takes a context, a token stream, and a tree context struct.
91 * Each returns a parse node tree or null on error.
92 */
93
94 typedef JSParseNode *
95 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
96
97 typedef JSParseNode *
98 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
99 JSBool allowCallSyntax);
100
101 typedef JSParseNode *
102 JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
103 JSTokenType tt, JSBool afterDot);
104
105 static JSParser FunctionStmt;
106 static JSParser FunctionExpr;
107 static JSParser Statements;
108 static JSParser Statement;
109 static JSParser Variables;
110 static JSParser Expr;
111 static JSParser AssignExpr;
112 static JSParser CondExpr;
113 static JSParser OrExpr;
114 static JSParser AndExpr;
115 static JSParser BitOrExpr;
116 static JSParser BitXorExpr;
117 static JSParser BitAndExpr;
118 static JSParser EqExpr;
119 static JSParser RelExpr;
120 static JSParser ShiftExpr;
121 static JSParser AddExpr;
122 static JSParser MulExpr;
123 static JSParser UnaryExpr;
124 static JSMemberParser MemberExpr;
125 static JSPrimaryParser PrimaryExpr;
126
127 /*
128 * Insist that the next token be of type tt, or report errno and return null.
129 * NB: this macro uses cx and ts from its lexical environment.
130 */
131 #define MUST_MATCH_TOKEN(tt, errno) \
132 JS_BEGIN_MACRO \
133 if (js_GetToken(cx, ts) != tt) { \
134 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
135 errno); \
136 return NULL; \
137 } \
138 JS_END_MACRO
139
140 #define CHECK_RECURSION() \
141 JS_BEGIN_MACRO \
142 int stackDummy; \
143 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \
144 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
145 JSMSG_OVER_RECURSED); \
146 return NULL; \
147 } \
148 JS_END_MACRO
149
150 #ifdef METER_PARSENODES
151 static uint32 parsenodes = 0;
152 static uint32 maxparsenodes = 0;
153 static uint32 recyclednodes = 0;
154 #endif
155
156 static JSParseNode *
RecycleTree(JSParseNode * pn,JSTreeContext * tc)157 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
158 {
159 JSParseNode *next;
160
161 if (!pn)
162 return NULL;
163 JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */
164 next = pn->pn_next;
165 pn->pn_next = tc->nodeList;
166 tc->nodeList = pn;
167 #ifdef METER_PARSENODES
168 recyclednodes++;
169 #endif
170 return next;
171 }
172
173 static JSParseNode *
NewOrRecycledNode(JSContext * cx,JSTreeContext * tc)174 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
175 {
176 JSParseNode *pn;
177
178 pn = tc->nodeList;
179 if (!pn) {
180 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
181 if (!pn)
182 JS_ReportOutOfMemory(cx);
183 } else {
184 tc->nodeList = pn->pn_next;
185
186 /* Recycle immediate descendents only, to save work and working set. */
187 switch (pn->pn_arity) {
188 case PN_FUNC:
189 RecycleTree(pn->pn_body, tc);
190 break;
191 case PN_LIST:
192 if (pn->pn_head) {
193 /* XXX check for dup recycles in the list */
194 *pn->pn_tail = tc->nodeList;
195 tc->nodeList = pn->pn_head;
196 #ifdef METER_PARSENODES
197 recyclednodes += pn->pn_count;
198 #endif
199 }
200 break;
201 case PN_TERNARY:
202 RecycleTree(pn->pn_kid1, tc);
203 RecycleTree(pn->pn_kid2, tc);
204 RecycleTree(pn->pn_kid3, tc);
205 break;
206 case PN_BINARY:
207 RecycleTree(pn->pn_left, tc);
208 RecycleTree(pn->pn_right, tc);
209 break;
210 case PN_UNARY:
211 RecycleTree(pn->pn_kid, tc);
212 break;
213 case PN_NAME:
214 RecycleTree(pn->pn_expr, tc);
215 break;
216 case PN_NULLARY:
217 break;
218 }
219 }
220 #ifdef METER_PARSENODES
221 if (pn) {
222 parsenodes++;
223 if (parsenodes - recyclednodes > maxparsenodes)
224 maxparsenodes = parsenodes - recyclednodes;
225 }
226 #endif
227 return pn;
228 }
229
230 /*
231 * Allocate a JSParseNode from cx's temporary arena.
232 */
233 static JSParseNode *
NewParseNode(JSContext * cx,JSTokenStream * ts,JSParseNodeArity arity,JSTreeContext * tc)234 NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
235 JSTreeContext *tc)
236 {
237 JSParseNode *pn;
238 JSToken *tp;
239
240 pn = NewOrRecycledNode(cx, tc);
241 if (!pn)
242 return NULL;
243 tp = &CURRENT_TOKEN(ts);
244 pn->pn_type = tp->type;
245 pn->pn_pos = tp->pos;
246 pn->pn_op = JSOP_NOP;
247 pn->pn_arity = arity;
248 pn->pn_next = NULL;
249 pn->pn_ts = ts;
250 pn->pn_source = NULL;
251 return pn;
252 }
253
254 static JSParseNode *
NewBinary(JSContext * cx,JSTokenType tt,JSOp op,JSParseNode * left,JSParseNode * right,JSTreeContext * tc)255 NewBinary(JSContext *cx, JSTokenType tt,
256 JSOp op, JSParseNode *left, JSParseNode *right,
257 JSTreeContext *tc)
258 {
259 JSParseNode *pn, *pn1, *pn2;
260
261 if (!left || !right)
262 return NULL;
263
264 /*
265 * Flatten a left-associative (left-heavy) tree of a given operator into
266 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
267 */
268 if (left->pn_type == tt &&
269 left->pn_op == op &&
270 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
271 if (left->pn_arity != PN_LIST) {
272 pn1 = left->pn_left, pn2 = left->pn_right;
273 left->pn_arity = PN_LIST;
274 PN_INIT_LIST_1(left, pn1);
275 PN_APPEND(left, pn2);
276 if (tt == TOK_PLUS) {
277 if (pn1->pn_type == TOK_STRING)
278 left->pn_extra |= PNX_STRCAT;
279 else if (pn1->pn_type != TOK_NUMBER)
280 left->pn_extra |= PNX_CANTFOLD;
281 if (pn2->pn_type == TOK_STRING)
282 left->pn_extra |= PNX_STRCAT;
283 else if (pn2->pn_type != TOK_NUMBER)
284 left->pn_extra |= PNX_CANTFOLD;
285 }
286 }
287 PN_APPEND(left, right);
288 left->pn_pos.end = right->pn_pos.end;
289 if (tt == TOK_PLUS) {
290 if (right->pn_type == TOK_STRING)
291 left->pn_extra |= PNX_STRCAT;
292 else if (right->pn_type != TOK_NUMBER)
293 left->pn_extra |= PNX_CANTFOLD;
294 }
295 return left;
296 }
297
298 /*
299 * Fold constant addition immediately, to conserve node space and, what's
300 * more, so js_FoldConstants never sees mixed addition and concatenation
301 * operations with more than one leading non-string operand in a PN_LIST
302 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
303 * to "3pt", not "12pt").
304 */
305 if (tt == TOK_PLUS &&
306 left->pn_type == TOK_NUMBER &&
307 right->pn_type == TOK_NUMBER) {
308 left->pn_dval += right->pn_dval;
309 left->pn_pos.end = right->pn_pos.end;
310 RecycleTree(right, tc);
311 return left;
312 }
313
314 pn = NewOrRecycledNode(cx, tc);
315 if (!pn)
316 return NULL;
317 pn->pn_type = tt;
318 pn->pn_pos.begin = left->pn_pos.begin;
319 pn->pn_pos.end = right->pn_pos.end;
320 pn->pn_op = op;
321 pn->pn_arity = PN_BINARY;
322 pn->pn_left = left;
323 pn->pn_right = right;
324 pn->pn_next = NULL;
325 pn->pn_ts = NULL;
326 pn->pn_source = NULL;
327 return pn;
328 }
329
330 #if JS_HAS_GETTER_SETTER
331 static JSTokenType
CheckGetterOrSetter(JSContext * cx,JSTokenStream * ts,JSTokenType tt)332 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
333 {
334 JSAtom *atom;
335 JSRuntime *rt;
336 JSOp op;
337 const char *name;
338
339 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
340 atom = CURRENT_TOKEN(ts).t_atom;
341 rt = cx->runtime;
342 if (atom == rt->atomState.getterAtom)
343 op = JSOP_GETTER;
344 else if (atom == rt->atomState.setterAtom)
345 op = JSOP_SETTER;
346 else
347 return TOK_NAME;
348 if (js_PeekTokenSameLine(cx, ts) != tt)
349 return TOK_NAME;
350 (void) js_GetToken(cx, ts);
351 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
352 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
353 JSMSG_BAD_GETTER_OR_SETTER,
354 (op == JSOP_GETTER)
355 ? js_getter_str
356 : js_setter_str);
357 return TOK_ERROR;
358 }
359 CURRENT_TOKEN(ts).t_op = op;
360 if (JS_HAS_STRICT_OPTION(cx)) {
361 name = js_AtomToPrintableString(cx, atom);
362 if (!name ||
363 !js_ReportCompileErrorNumber(cx, ts,
364 JSREPORT_TS |
365 JSREPORT_WARNING |
366 JSREPORT_STRICT,
367 JSMSG_DEPRECATED_USAGE,
368 name)) {
369 return TOK_ERROR;
370 }
371 }
372 return tt;
373 }
374 #endif
375
376 static void
MaybeSetupFrame(JSContext * cx,JSObject * chain,JSStackFrame * oldfp,JSStackFrame * newfp)377 MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp,
378 JSStackFrame *newfp)
379 {
380 /*
381 * Always push a new frame if the current frame is special, so that
382 * Variables gets the correct variables object: the one from the special
383 * frame's caller.
384 */
385 if (oldfp &&
386 oldfp->varobj &&
387 oldfp->scopeChain == chain &&
388 !(oldfp->flags & JSFRAME_SPECIAL)) {
389 return;
390 }
391
392 memset(newfp, 0, sizeof *newfp);
393
394 /* Default to sharing the same variables object and scope chain. */
395 newfp->varobj = newfp->scopeChain = chain;
396 if (cx->options & JSOPTION_VAROBJFIX) {
397 while ((chain = JS_GetParent(cx, chain)) != NULL)
398 newfp->varobj = chain;
399 }
400 newfp->down = oldfp;
401 if (oldfp) {
402 /*
403 * In the case of eval and debugger frames, we need to dig down and find
404 * the real variables objects and function that our new stack frame is
405 * going to use.
406 */
407 newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
408 JSFRAME_SCRIPT_OBJECT);
409 while (oldfp->flags & JSFRAME_SPECIAL) {
410 oldfp = oldfp->down;
411 if (!oldfp)
412 break;
413 }
414 if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) {
415 newfp->varobj = oldfp->varobj;
416 newfp->vars = oldfp->vars;
417 newfp->fun = oldfp->fun;
418 }
419 }
420 cx->fp = newfp;
421 }
422
423 /*
424 * Parse a top-level JS script.
425 */
426 JS_FRIEND_API(JSParseNode *)
js_ParseTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts)427 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
428 {
429 JSStackFrame *fp, frame;
430 JSTreeContext tc;
431 JSParseNode *pn;
432
433 /*
434 * Push a compiler frame if we have no frames, or if the top frame is a
435 * lightweight function activation, or if its scope chain doesn't match
436 * the one passed to us.
437 */
438 fp = cx->fp;
439 MaybeSetupFrame(cx, chain, fp, &frame);
440
441 /*
442 * Protect atoms from being collected by a GC activation, which might
443 * - nest on this thread due to out of memory (the so-called "last ditch"
444 * GC attempted within js_NewGCThing), or
445 * - run for any reason on another thread if this thread is suspended on
446 * an object lock before it finishes generating bytecode into a script
447 * protected from the GC by a root or a stack frame reference.
448 */
449 JS_KEEP_ATOMS(cx->runtime);
450 TREE_CONTEXT_INIT(&tc);
451 pn = Statements(cx, ts, &tc);
452 if (pn) {
453 if (!js_MatchToken(cx, ts, TOK_EOF)) {
454 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
455 JSMSG_SYNTAX_ERROR);
456 pn = NULL;
457 } else {
458 pn->pn_type = TOK_LC;
459 if (!js_FoldConstants(cx, pn, &tc))
460 pn = NULL;
461 }
462 }
463
464 TREE_CONTEXT_FINISH(&tc);
465 JS_UNKEEP_ATOMS(cx->runtime);
466 cx->fp = fp;
467 return pn;
468 }
469
470 /*
471 * Compile a top-level script.
472 */
473 JS_FRIEND_API(JSBool)
js_CompileTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts,JSCodeGenerator * cg)474 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
475 JSCodeGenerator *cg)
476 {
477 JSStackFrame *fp, frame;
478 uint32 flags;
479 JSParseNode *pn;
480 JSBool ok;
481 #ifdef METER_PARSENODES
482 void *sbrk(ptrdiff_t), *before = sbrk(0);
483 #endif
484
485 /*
486 * Push a compiler frame if we have no frames, or if the top frame is a
487 * lightweight function activation, or if its scope chain doesn't match
488 * the one passed to us.
489 */
490 fp = cx->fp;
491 MaybeSetupFrame(cx, chain, fp, &frame);
492 flags = cx->fp->flags;
493 cx->fp->flags = flags |
494 (JS_HAS_COMPILE_N_GO_OPTION(cx)
495 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
496 : JSFRAME_COMPILING);
497
498 /* Prevent GC activation while compiling. */
499 JS_KEEP_ATOMS(cx->runtime);
500
501 pn = Statements(cx, ts, &cg->treeContext);
502 if (!pn) {
503 ok = JS_FALSE;
504 } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
505 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
506 JSMSG_SYNTAX_ERROR);
507 ok = JS_FALSE;
508 } else {
509 #ifdef METER_PARSENODES
510 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
511 (char *)sbrk(0) - (char *)before,
512 parsenodes,
513 maxparsenodes,
514 parsenodes - recyclednodes);
515 before = sbrk(0);
516 #endif
517
518 /*
519 * No need to emit bytecode here -- Statements already has, for each
520 * statement in turn. Search for TCF_COMPILING in Statements, below.
521 * That flag is set for every tc == &cg->treeContext, and it implies
522 * that the tc can be downcast to a cg and used to emit code during
523 * parsing, rather than at the end of the parse phase.
524 *
525 * Nowadays the threaded interpreter needs a stop instruction, so we
526 * do have to emit that here.
527 */
528 JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
529 ok = js_Emit1(cx, cg, JSOP_STOP) >= 0;
530 }
531
532 #ifdef METER_PARSENODES
533 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
534 (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
535 #endif
536 #ifdef JS_ARENAMETER
537 JS_DumpArenaStats(stdout);
538 #endif
539 JS_UNKEEP_ATOMS(cx->runtime);
540 cx->fp->flags = flags;
541 cx->fp = fp;
542 return ok;
543 }
544
545 /*
546 * Insist on a final return before control flows out of pn. Try to be a bit
547 * smart about loops: do {...; return e2;} while(0) at the end of a function
548 * that contains an early return e1 will get a strict warning. Similarly for
549 * iloops: while (true){...} is treated as though ... returns.
550 */
551 #define ENDS_IN_OTHER 0
552 #define ENDS_IN_RETURN 1
553 #define ENDS_IN_BREAK 2
554
555 static int
HasFinalReturn(JSParseNode * pn)556 HasFinalReturn(JSParseNode *pn)
557 {
558 JSParseNode *pn2, *pn3;
559 uintN rv, rv2, hasDefault;
560
561 switch (pn->pn_type) {
562 case TOK_LC:
563 if (!pn->pn_head)
564 return ENDS_IN_OTHER;
565 return HasFinalReturn(PN_LAST(pn));
566
567 case TOK_IF:
568 if (!pn->pn_kid3)
569 return ENDS_IN_OTHER;
570 return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
571
572 case TOK_WHILE:
573 pn2 = pn->pn_left;
574 if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
575 return ENDS_IN_RETURN;
576 if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
577 return ENDS_IN_RETURN;
578 return ENDS_IN_OTHER;
579
580 case TOK_DO:
581 pn2 = pn->pn_right;
582 if (pn2->pn_type == TOK_PRIMARY) {
583 if (pn2->pn_op == JSOP_FALSE)
584 return HasFinalReturn(pn->pn_left);
585 if (pn2->pn_op == JSOP_TRUE)
586 return ENDS_IN_RETURN;
587 }
588 if (pn2->pn_type == TOK_NUMBER) {
589 if (pn2->pn_dval == 0)
590 return HasFinalReturn(pn->pn_left);
591 return ENDS_IN_RETURN;
592 }
593 return ENDS_IN_OTHER;
594
595 case TOK_FOR:
596 pn2 = pn->pn_left;
597 if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
598 return ENDS_IN_RETURN;
599 return ENDS_IN_OTHER;
600
601 case TOK_SWITCH:
602 rv = ENDS_IN_RETURN;
603 hasDefault = ENDS_IN_OTHER;
604 pn2 = pn->pn_right;
605 if (pn2->pn_type == TOK_LEXICALSCOPE)
606 pn2 = pn2->pn_expr;
607 for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
608 if (pn2->pn_type == TOK_DEFAULT)
609 hasDefault = ENDS_IN_RETURN;
610 pn3 = pn2->pn_right;
611 JS_ASSERT(pn3->pn_type == TOK_LC);
612 if (pn3->pn_head) {
613 rv2 = HasFinalReturn(PN_LAST(pn3));
614 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
615 /* Falling through to next case or default. */;
616 else
617 rv &= rv2;
618 }
619 }
620 /* If a final switch has no default case, we judge it harshly. */
621 rv &= hasDefault;
622 return rv;
623
624 case TOK_BREAK:
625 return ENDS_IN_BREAK;
626
627 case TOK_WITH:
628 return HasFinalReturn(pn->pn_right);
629
630 case TOK_RETURN:
631 return ENDS_IN_RETURN;
632
633 case TOK_COLON:
634 case TOK_LEXICALSCOPE:
635 return HasFinalReturn(pn->pn_expr);
636
637 case TOK_THROW:
638 return ENDS_IN_RETURN;
639
640 case TOK_TRY:
641 /* If we have a finally block that returns, we are done. */
642 if (pn->pn_kid3) {
643 rv = HasFinalReturn(pn->pn_kid3);
644 if (rv == ENDS_IN_RETURN)
645 return rv;
646 }
647
648 /* Else check the try block and any and all catch statements. */
649 rv = HasFinalReturn(pn->pn_kid1);
650 if (pn->pn_kid2) {
651 JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST);
652 for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next)
653 rv &= HasFinalReturn(pn2);
654 }
655 return rv;
656
657 case TOK_CATCH:
658 /* Check this catch block's body. */
659 return HasFinalReturn(pn->pn_kid3);
660
661 case TOK_LET:
662 /* Non-binary let statements are let declarations. */
663 if (pn->pn_arity != PN_BINARY)
664 return ENDS_IN_OTHER;
665 return HasFinalReturn(pn->pn_right);
666
667 default:
668 return ENDS_IN_OTHER;
669 }
670 }
671
672 static JSBool
ReportBadReturn(JSContext * cx,JSTokenStream * ts,uintN flags,uintN errnum,uintN anonerrnum)673 ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum,
674 uintN anonerrnum)
675 {
676 JSFunction *fun;
677 const char *name;
678
679 fun = cx->fp->fun;
680 if (fun->atom) {
681 name = js_AtomToPrintableString(cx, fun->atom);
682 } else {
683 errnum = anonerrnum;
684 name = NULL;
685 }
686 return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum,
687 name);
688 }
689
690 static JSBool
CheckFinalReturn(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)691 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
692 {
693 return HasFinalReturn(pn) == ENDS_IN_RETURN ||
694 ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
695 JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
696 }
697
698 static JSParseNode *
FunctionBody(JSContext * cx,JSTokenStream * ts,JSFunction * fun,JSTreeContext * tc)699 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
700 JSTreeContext *tc)
701 {
702 JSStackFrame *fp, frame;
703 JSObject *funobj;
704 JSStmtInfo stmtInfo;
705 uintN oldflags, firstLine;
706 JSParseNode *pn;
707
708 fp = cx->fp;
709 funobj = fun->object;
710 if (!fp || fp->fun != fun || fp->varobj != funobj ||
711 fp->scopeChain != funobj) {
712 memset(&frame, 0, sizeof frame);
713 frame.fun = fun;
714 frame.varobj = frame.scopeChain = funobj;
715 frame.down = fp;
716 if (fp)
717 frame.flags = fp->flags & JSFRAME_COMPILE_N_GO;
718 cx->fp = &frame;
719 }
720
721 /*
722 * Set interpreted early so js_EmitTree can test it to decide whether to
723 * eliminate useless expressions.
724 */
725 fun->flags |= JSFUN_INTERPRETED;
726
727 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
728 stmtInfo.flags = SIF_BODY_BLOCK;
729
730 oldflags = tc->flags;
731 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
732 tc->flags |= TCF_IN_FUNCTION;
733
734 /*
735 * Save the body's first line, and store it in pn->pn_pos.begin.lineno
736 * later, because we may have not peeked in ts yet, so Statements won't
737 * acquire a valid pn->pn_pos.begin from the current token.
738 */
739 firstLine = ts->lineno;
740 pn = Statements(cx, ts, tc);
741
742 js_PopStatement(tc);
743
744 /* Check for falling off the end of a function that returns a value. */
745 if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
746 if (!CheckFinalReturn(cx, ts, pn))
747 pn = NULL;
748 }
749
750 /*
751 * If we have a parse tree in pn and a code generator in tc, emit this
752 * function's code. We must do this here, not in js_CompileFunctionBody,
753 * in order to detect TCF_IN_FUNCTION among tc->flags.
754 */
755 if (pn) {
756 pn->pn_pos.begin.lineno = firstLine;
757 if ((tc->flags & TCF_COMPILING)) {
758 JSCodeGenerator *cg = (JSCodeGenerator *) tc;
759
760 if (!js_FoldConstants(cx, pn, tc) ||
761 !js_EmitFunctionBytecode(cx, cg, pn)) {
762 pn = NULL;
763 }
764 }
765 }
766
767 cx->fp = fp;
768 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
769 return pn;
770 }
771
772 /*
773 * Compile a JS function body, which might appear as the value of an event
774 * handler attribute in an HTML <INPUT> tag.
775 */
776 JSBool
js_CompileFunctionBody(JSContext * cx,JSTokenStream * ts,JSFunction * fun)777 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
778 {
779 JSArenaPool codePool, notePool;
780 JSCodeGenerator funcg;
781 JSStackFrame *fp, frame;
782 JSObject *funobj;
783 JSParseNode *pn;
784
785 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
786 JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote));
787 if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool,
788 ts->filename, ts->lineno,
789 ts->principals)) {
790 return JS_FALSE;
791 }
792
793 /* Prevent GC activation while compiling. */
794 JS_KEEP_ATOMS(cx->runtime);
795
796 /* Push a JSStackFrame for use by FunctionBody. */
797 fp = cx->fp;
798 funobj = fun->object;
799 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
800 fp->scopeChain != funobj));
801 memset(&frame, 0, sizeof frame);
802 frame.fun = fun;
803 frame.varobj = frame.scopeChain = funobj;
804 frame.down = fp;
805 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
806 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
807 : JSFRAME_COMPILING;
808 cx->fp = &frame;
809
810 /*
811 * Farble the body so that it looks like a block statement to js_EmitTree,
812 * which is called beneath FunctionBody; see Statements, further below in
813 * this file. FunctionBody pushes a STMT_BLOCK record around its call to
814 * Statements, so Statements will not compile each statement as it loops
815 * to save JSParseNode space -- it will not compile at all, only build a
816 * JSParseNode tree.
817 *
818 * Therefore we must fold constants, allocate try notes, and generate code
819 * for this function, including a stop opcode at the end.
820 */
821 CURRENT_TOKEN(ts).type = TOK_LC;
822 pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
823 if (pn && !js_NewScriptFromCG(cx, &funcg, fun))
824 pn = NULL;
825
826 /* Restore saved state and release code generation arenas. */
827 cx->fp = fp;
828 JS_UNKEEP_ATOMS(cx->runtime);
829 js_FinishCodeGenerator(cx, &funcg);
830 JS_FinishArenaPool(&codePool);
831 JS_FinishArenaPool(¬ePool);
832 return pn != NULL;
833 }
834
835 /*
836 * Parameter block types for the several Binder functions. We use a common
837 * helper function signature in order to share code among destructuring and
838 * simple variable declaration parsers. In the destructuring case, the binder
839 * function is called indirectly from the variable declaration parser by way
840 * of CheckDestructuring and its friends.
841 */
842 typedef struct BindData BindData;
843
844 typedef JSBool
845 (*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc);
846
847 struct BindData {
848 JSParseNode *pn; /* error source coordinate */
849 JSTokenStream *ts; /* fallback if pn is null */
850 JSObject *obj; /* the variable object */
851 JSOp op; /* prolog bytecode or nop */
852 Binder binder; /* binder, discriminates u */
853 union {
854 struct {
855 JSFunction *fun; /* must come first! see next */
856 } arg;
857 struct {
858 JSFunction *fun; /* this overlays u.arg.fun */
859 JSClass *clasp;
860 JSPropertyOp getter;
861 JSPropertyOp setter;
862 uintN attrs;
863 } var;
864 struct {
865 jsuint index;
866 uintN overflow;
867 } let;
868 } u;
869 };
870
871 /*
872 * Given BindData *data and JSREPORT_* flags, expand to the second and third
873 * actual parameters to js_ReportCompileErrorNumber. Prefer reporting via pn
874 * to reporting via ts, for better destructuring error pointers.
875 */
876 #define BIND_DATA_REPORT_ARGS(data, flags) \
877 (data)->pn ? (void *)(data)->pn : (void *)(data)->ts, \
878 ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags)
879
880 static JSBool
BumpFormalCount(JSContext * cx,JSFunction * fun)881 BumpFormalCount(JSContext *cx, JSFunction *fun)
882 {
883 if (fun->nargs == JS_BITMASK(16)) {
884 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
885 JSMSG_TOO_MANY_FUN_ARGS);
886 return JS_FALSE;
887 }
888 fun->nargs++;
889 return JS_TRUE;
890 }
891
892 static JSBool
BindArg(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)893 BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
894 {
895 JSObject *obj, *pobj;
896 JSProperty *prop;
897 JSBool ok;
898 uintN dupflag;
899 JSFunction *fun;
900 const char *name;
901
902 obj = data->obj;
903 ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
904 if (!ok)
905 return JS_FALSE;
906
907 dupflag = 0;
908 if (prop) {
909 JS_ASSERT(pobj == obj);
910 name = js_AtomToPrintableString(cx, atom);
911
912 /*
913 * A duplicate parameter name, a "feature" required by ECMA-262.
914 * We force a duplicate node on the SCOPE_LAST_PROP(scope) list
915 * with the same id, distinguished by the SPROP_IS_DUPLICATE flag,
916 * and not mapped by an entry in scope.
917 */
918 ok = name &&
919 js_ReportCompileErrorNumber(cx,
920 BIND_DATA_REPORT_ARGS(data,
921 JSREPORT_WARNING |
922 JSREPORT_STRICT),
923 JSMSG_DUPLICATE_FORMAL,
924 name);
925
926 OBJ_DROP_PROPERTY(cx, pobj, prop);
927 if (!ok)
928 return JS_FALSE;
929
930 dupflag = SPROP_IS_DUPLICATE;
931 }
932
933 fun = data->u.arg.fun;
934 if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
935 js_GetArgument, js_SetArgument,
936 SPROP_INVALID_SLOT,
937 JSPROP_PERMANENT | JSPROP_SHARED,
938 dupflag | SPROP_HAS_SHORTID,
939 fun->nargs)) {
940 return JS_FALSE;
941 }
942
943 return BumpFormalCount(cx, fun);
944 }
945
946 static JSBool
BindLocalVariable(JSContext * cx,BindData * data,JSAtom * atom)947 BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom)
948 {
949 JSFunction *fun;
950
951 /*
952 * Can't increase fun->nvars in an active frame, so insist that getter is
953 * js_GetLocalVariable, not js_GetCallVariable or anything else.
954 */
955 if (data->u.var.getter != js_GetLocalVariable)
956 return JS_TRUE;
957
958 /*
959 * Don't bind a variable with the hidden name 'arguments', per ECMA-262.
960 * Instead 'var arguments' always restates the predefined property of the
961 * activation objects with unhidden name 'arguments'. Assignment to such
962 * a variable must be handled specially.
963 */
964 if (atom == cx->runtime->atomState.argumentsAtom)
965 return JS_TRUE;
966
967 fun = data->u.var.fun;
968 if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom),
969 data->u.var.getter, data->u.var.setter,
970 SPROP_INVALID_SLOT,
971 data->u.var.attrs | JSPROP_SHARED,
972 SPROP_HAS_SHORTID, fun->u.i.nvars)) {
973 return JS_FALSE;
974 }
975 if (fun->u.i.nvars == JS_BITMASK(16)) {
976 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
977 JSMSG_TOO_MANY_FUN_VARS);
978 return JS_FALSE;
979 }
980 fun->u.i.nvars++;
981 return JS_TRUE;
982 }
983
984 #if JS_HAS_DESTRUCTURING
985 /*
986 * Forward declaration to maintain top-down presentation.
987 */
988 static JSParseNode *
989 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
990 JSTokenType tt);
991
992 static JSBool
BindDestructuringArg(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)993 BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom,
994 JSTreeContext *tc)
995 {
996 JSAtomListElement *ale;
997 JSFunction *fun;
998 JSObject *obj, *pobj;
999 JSProperty *prop;
1000 const char *name;
1001
1002 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1003 if (!ale) {
1004 ale = js_IndexAtom(cx, atom, &tc->decls);
1005 if (!ale)
1006 return JS_FALSE;
1007 ALE_SET_JSOP(ale, data->op);
1008 }
1009
1010 fun = data->u.var.fun;
1011 obj = data->obj;
1012 if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
1013 return JS_FALSE;
1014
1015 if (prop) {
1016 JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj));
1017 name = js_AtomToPrintableString(cx, atom);
1018 if (!name ||
1019 !js_ReportCompileErrorNumber(cx,
1020 BIND_DATA_REPORT_ARGS(data,
1021 JSREPORT_WARNING |
1022 JSREPORT_STRICT),
1023 JSMSG_DUPLICATE_FORMAL,
1024 name)) {
1025 return JS_FALSE;
1026 }
1027 OBJ_DROP_PROPERTY(cx, pobj, prop);
1028 } else {
1029 if (!BindLocalVariable(cx, data, atom))
1030 return JS_FALSE;
1031 }
1032 return JS_TRUE;
1033 }
1034 #endif /* JS_HAS_DESTRUCTURING */
1035
1036 static JSParseNode *
FunctionDef(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool lambda)1037 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1038 JSBool lambda)
1039 {
1040 JSOp op, prevop;
1041 JSParseNode *pn, *body, *result;
1042 JSTokenType tt;
1043 JSAtom *funAtom, *objAtom;
1044 JSStackFrame *fp;
1045 JSObject *varobj, *pobj;
1046 JSAtomListElement *ale;
1047 JSProperty *prop;
1048 JSFunction *fun;
1049 JSTreeContext funtc;
1050 #if JS_HAS_DESTRUCTURING
1051 JSParseNode *item, *list = NULL;
1052 #endif
1053
1054 /* Make a TOK_FUNCTION node. */
1055 #if JS_HAS_GETTER_SETTER
1056 op = CURRENT_TOKEN(ts).t_op;
1057 #endif
1058 pn = NewParseNode(cx, ts, PN_FUNC, tc);
1059 if (!pn)
1060 return NULL;
1061
1062 /* Scan the optional function name into funAtom. */
1063 ts->flags |= TSF_KEYWORD_IS_NAME;
1064 tt = js_GetToken(cx, ts);
1065 ts->flags &= ~TSF_KEYWORD_IS_NAME;
1066 if (tt == TOK_NAME) {
1067 funAtom = CURRENT_TOKEN(ts).t_atom;
1068 } else {
1069 funAtom = NULL;
1070 js_UngetToken(ts);
1071 }
1072
1073 /* Find the nearest variable-declaring scope and use it as our parent. */
1074 fp = cx->fp;
1075 varobj = fp->varobj;
1076
1077 /*
1078 * Record names for function statements in tc->decls so we know when to
1079 * avoid optimizing variable references that might name a function.
1080 */
1081 if (!lambda && funAtom) {
1082 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
1083 if (ale) {
1084 prevop = ALE_JSOP(ale);
1085 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1086 const char *name = js_AtomToPrintableString(cx, funAtom);
1087 if (!name ||
1088 !js_ReportCompileErrorNumber(cx, ts,
1089 (prevop != JSOP_DEFCONST)
1090 ? JSREPORT_TS |
1091 JSREPORT_WARNING |
1092 JSREPORT_STRICT
1093 : JSREPORT_TS | JSREPORT_ERROR,
1094 JSMSG_REDECLARED_VAR,
1095 (prevop == JSOP_DEFFUN ||
1096 prevop == JSOP_CLOSURE)
1097 ? js_function_str
1098 : (prevop == JSOP_DEFCONST)
1099 ? js_const_str
1100 : js_var_str,
1101 name)) {
1102 return NULL;
1103 }
1104 }
1105 if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR)
1106 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1107 } else {
1108 ale = js_IndexAtom(cx, funAtom, &tc->decls);
1109 if (!ale)
1110 return NULL;
1111 }
1112 ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE);
1113
1114 /*
1115 * A function nested at top level inside another's body needs only a
1116 * local variable to bind its name to its value, and not an activation
1117 * object property (it might also need the activation property, if the
1118 * outer function contains with statements, e.g., but the stack slot
1119 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
1120 * JSOP_GETVAR bytecode).
1121 */
1122 if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) {
1123 JSScopeProperty *sprop;
1124
1125 /*
1126 * Define a property on the outer function so that BindNameToSlot
1127 * can properly optimize accesses.
1128 */
1129 JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
1130 JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
1131 if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
1132 &pobj, &prop)) {
1133 return NULL;
1134 }
1135 if (prop)
1136 OBJ_DROP_PROPERTY(cx, pobj, prop);
1137 sprop = NULL;
1138 if (!prop ||
1139 pobj != varobj ||
1140 (sprop = (JSScopeProperty *)prop,
1141 sprop->getter != js_GetLocalVariable)) {
1142 uintN sflags;
1143
1144 /*
1145 * Use SPROP_IS_DUPLICATE if there is a formal argument of the
1146 * same name, so the decompiler can find the parameter name.
1147 */
1148 sflags = (sprop && sprop->getter == js_GetArgument)
1149 ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID
1150 : SPROP_HAS_SHORTID;
1151 if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
1152 js_GetLocalVariable,
1153 js_SetLocalVariable,
1154 SPROP_INVALID_SLOT,
1155 JSPROP_PERMANENT | JSPROP_SHARED,
1156 sflags, fp->fun->u.i.nvars)) {
1157 return NULL;
1158 }
1159 if (fp->fun->u.i.nvars == JS_BITMASK(16)) {
1160 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1161 JSMSG_TOO_MANY_FUN_VARS);
1162 return NULL;
1163 }
1164 fp->fun->u.i.nvars++;
1165 }
1166 }
1167 }
1168
1169 fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
1170 funAtom);
1171 if (!fun)
1172 return NULL;
1173 #if JS_HAS_GETTER_SETTER
1174 if (op != JSOP_NOP)
1175 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1176 #endif
1177
1178 /*
1179 * Atomize fun->object early to protect against a last-ditch GC under
1180 * js_LookupHiddenProperty.
1181 *
1182 * Absent use of the new scoped local GC roots API around compiler calls,
1183 * we need to atomize here to protect against a GC activation. Atoms are
1184 * protected from GC during compilation by the JS_FRIEND_API entry points
1185 * in this file. There doesn't seem to be any gain in switching from the
1186 * atom-keeping method to the bulkier, slower scoped local roots method.
1187 */
1188 objAtom = js_AtomizeObject(cx, fun->object, 0);
1189 if (!objAtom)
1190 return NULL;
1191
1192 /* Initialize early for possible flags mutation via DestructuringExpr. */
1193 TREE_CONTEXT_INIT(&funtc);
1194
1195 /* Now parse formal argument list and compute fun->nargs. */
1196 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
1197 if (!js_MatchToken(cx, ts, TOK_RP)) {
1198 BindData data;
1199
1200 data.pn = NULL;
1201 data.ts = ts;
1202 data.obj = fun->object;
1203 data.op = JSOP_NOP;
1204 data.binder = BindArg;
1205 data.u.arg.fun = fun;
1206
1207 do {
1208 tt = js_GetToken(cx, ts);
1209 switch (tt) {
1210 #if JS_HAS_DESTRUCTURING
1211 case TOK_LB:
1212 case TOK_LC:
1213 {
1214 JSParseNode *lhs, *rhs;
1215 jsint slot;
1216
1217 /*
1218 * A destructuring formal parameter turns into one or more
1219 * local variables initialized from properties of a single
1220 * anonymous positional parameter, so here we must tweak our
1221 * binder and its data.
1222 */
1223 data.op = JSOP_DEFVAR;
1224 data.binder = BindDestructuringArg;
1225 data.u.var.clasp = &js_FunctionClass;
1226 data.u.var.getter = js_GetLocalVariable;
1227 data.u.var.setter = js_SetLocalVariable;
1228 data.u.var.attrs = JSPROP_PERMANENT;
1229
1230 /*
1231 * Temporarily transfer the owneship of the recycle list to
1232 * funtc. See bug 313967.
1233 */
1234 funtc.nodeList = tc->nodeList;
1235 tc->nodeList = NULL;
1236 lhs = DestructuringExpr(cx, &data, &funtc, tt);
1237 tc->nodeList = funtc.nodeList;
1238 funtc.nodeList = NULL;
1239 if (!lhs)
1240 return NULL;
1241
1242 /*
1243 * Restore the formal parameter binder in case there are more
1244 * non-destructuring formals in the parameter list.
1245 */
1246 data.binder = BindArg;
1247
1248 /*
1249 * Adjust fun->nargs to count the single anonymous positional
1250 * parameter that is to be destructured.
1251 */
1252 slot = fun->nargs;
1253 if (!BumpFormalCount(cx, fun))
1254 return NULL;
1255
1256 /*
1257 * Synthesize a destructuring assignment from the single
1258 * anonymous positional parameter into the destructuring
1259 * left-hand-side expression and accumulate it in list.
1260 */
1261 rhs = NewParseNode(cx, ts, PN_NAME, tc);
1262 if (!rhs)
1263 return NULL;
1264 rhs->pn_type = TOK_NAME;
1265 rhs->pn_op = JSOP_GETARG;
1266 rhs->pn_atom = cx->runtime->atomState.emptyAtom;
1267 rhs->pn_expr = NULL;
1268 rhs->pn_slot = slot;
1269 rhs->pn_attrs = 0;
1270
1271 item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc);
1272 if (!item)
1273 return NULL;
1274 if (!list) {
1275 list = NewParseNode(cx, ts, PN_LIST, tc);
1276 if (!list)
1277 return NULL;
1278 list->pn_type = TOK_COMMA;
1279 PN_INIT_LIST(list);
1280 }
1281 PN_APPEND(list, item);
1282 break;
1283 }
1284 #endif /* JS_HAS_DESTRUCTURING */
1285
1286 case TOK_NAME:
1287 if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc))
1288 return NULL;
1289 break;
1290
1291 default:
1292 js_ReportCompileErrorNumber(cx, ts,
1293 JSREPORT_TS | JSREPORT_ERROR,
1294 JSMSG_MISSING_FORMAL);
1295 return NULL;
1296 }
1297 } while (js_MatchToken(cx, ts, TOK_COMMA));
1298
1299 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
1300 }
1301
1302 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1303 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1304
1305 /*
1306 * Temporarily transfer the owneship of the recycle list to funtc.
1307 * See bug 313967.
1308 */
1309 funtc.nodeList = tc->nodeList;
1310 tc->nodeList = NULL;
1311 body = FunctionBody(cx, ts, fun, &funtc);
1312 tc->nodeList = funtc.nodeList;
1313 funtc.nodeList = NULL;
1314
1315 if (!body)
1316 return NULL;
1317
1318 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1319 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1320
1321 #if JS_HAS_DESTRUCTURING
1322 /*
1323 * If there were destructuring formal parameters, prepend the initializing
1324 * comma expression that we synthesized to body. If the body is a lexical
1325 * scope node, we must make a special TOK_BODY node, to prepend the formal
1326 * parameter destructuring code without bracing the decompilation of the
1327 * function body's lexical scope.
1328 */
1329 if (list) {
1330 if (body->pn_arity != PN_LIST) {
1331 JSParseNode *block;
1332
1333 JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE);
1334 JS_ASSERT(body->pn_arity == PN_NAME);
1335
1336 block = NewParseNode(cx, ts, PN_LIST, tc);
1337 if (!block)
1338 return NULL;
1339 block->pn_type = TOK_BODY;
1340 block->pn_pos = body->pn_pos;
1341 PN_INIT_LIST_1(block, body);
1342
1343 body = block;
1344 }
1345
1346 item = NewParseNode(cx, ts, PN_UNARY, tc);
1347 if (!item)
1348 return NULL;
1349
1350 item->pn_type = TOK_SEMI;
1351 item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin;
1352 item->pn_kid = list;
1353 item->pn_next = body->pn_head;
1354 body->pn_head = item;
1355 if (body->pn_tail == &body->pn_head)
1356 body->pn_tail = &item->pn_next;
1357 ++body->pn_count;
1358 }
1359 #endif
1360
1361 /*
1362 * If we collected flags that indicate nested heavyweight functions, or
1363 * this function contains heavyweight-making statements (references to
1364 * __parent__ or __proto__; use of with, eval, import, or export; and
1365 * assignment to arguments), flag the function as heavyweight (requiring
1366 * a call object per invocation).
1367 */
1368 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1369 fun->flags |= JSFUN_HEAVYWEIGHT;
1370 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1371 } else {
1372 /*
1373 * If this function is a named statement function not at top-level
1374 * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then
1375 * our enclosing function, if any, must be heavyweight.
1376 *
1377 * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator,
1378 * so it won't be set here. Assert that it's not. We have to check
1379 * it later, in js_EmitTree, after js_EmitFunctionBody has traversed
1380 * the function's body
1381 */
1382 JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS));
1383 if (!lambda && funAtom && !AT_TOP_LEVEL(tc))
1384 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1385 }
1386
1387 result = pn;
1388 if (lambda) {
1389 /*
1390 * ECMA ed. 3 standard: function expression, possibly anonymous.
1391 */
1392 op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1393 } else if (!funAtom) {
1394 /*
1395 * If this anonymous function definition is *not* embedded within a
1396 * larger expression, we treat it as an expression statement, not as
1397 * a function declaration -- and not as a syntax error (as ECMA-262
1398 * Edition 3 would have it). Backward compatibility trumps all.
1399 */
1400 result = NewParseNode(cx, ts, PN_UNARY, tc);
1401 if (!result)
1402 return NULL;
1403 result->pn_type = TOK_SEMI;
1404 result->pn_pos = pn->pn_pos;
1405 result->pn_kid = pn;
1406 op = JSOP_ANONFUNOBJ;
1407 } else if (!AT_TOP_LEVEL(tc)) {
1408 /*
1409 * ECMA ed. 3 extension: a function expression statement not at the
1410 * top level, e.g., in a compound statement such as the "then" part
1411 * of an "if" statement, binds a closure only if control reaches that
1412 * sub-statement.
1413 */
1414 op = JSOP_CLOSURE;
1415 } else {
1416 op = JSOP_NOP;
1417 }
1418
1419 pn->pn_funAtom = objAtom;
1420 pn->pn_op = op;
1421 pn->pn_body = body;
1422 pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
1423 pn->pn_tryCount = funtc.tryCount;
1424 TREE_CONTEXT_FINISH(&funtc);
1425 return result;
1426 }
1427
1428 static JSParseNode *
FunctionStmt(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1429 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1430 {
1431 return FunctionDef(cx, ts, tc, JS_FALSE);
1432 }
1433
1434 static JSParseNode *
FunctionExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1435 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1436 {
1437 return FunctionDef(cx, ts, tc, JS_TRUE);
1438 }
1439
1440 /*
1441 * Parse the statements in a block, creating a TOK_LC node that lists the
1442 * statements' trees. If called from block-parsing code, the caller must
1443 * match { before and } after.
1444 */
1445 static JSParseNode *
Statements(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1446 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1447 {
1448 JSParseNode *pn, *pn2, *saveBlock;
1449 JSTokenType tt;
1450
1451 CHECK_RECURSION();
1452
1453 pn = NewParseNode(cx, ts, PN_LIST, tc);
1454 if (!pn)
1455 return NULL;
1456 saveBlock = tc->blockNode;
1457 tc->blockNode = pn;
1458 PN_INIT_LIST(pn);
1459
1460 ts->flags |= TSF_OPERAND;
1461 while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
1462 ts->flags &= ~TSF_OPERAND;
1463 pn2 = Statement(cx, ts, tc);
1464 if (!pn2) {
1465 if (ts->flags & TSF_EOF)
1466 ts->flags |= TSF_UNEXPECTED_EOF;
1467 return NULL;
1468 }
1469 ts->flags |= TSF_OPERAND;
1470
1471 /* Detect a function statement for the TOK_LC case in Statement. */
1472 if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc))
1473 tc->flags |= TCF_HAS_FUNCTION_STMT;
1474
1475 /* If compiling top-level statements, emit as we go to save space. */
1476 if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1477 if (cx->fp->fun &&
1478 JS_HAS_STRICT_OPTION(cx) &&
1479 (tc->flags & TCF_RETURN_EXPR)) {
1480 /*
1481 * Check pn2 for lack of a final return statement if it is the
1482 * last statement in the block.
1483 */
1484 tt = js_PeekToken(cx, ts);
1485 if ((tt == TOK_EOF || tt == TOK_RC) &&
1486 !CheckFinalReturn(cx, ts, pn2)) {
1487 tt = TOK_ERROR;
1488 break;
1489 }
1490
1491 /*
1492 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1493 * CheckFinalReturn again.
1494 */
1495 tc->flags &= ~TCF_RETURN_EXPR;
1496 }
1497 if (!js_FoldConstants(cx, pn2, tc) ||
1498 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1499 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1500 tt = TOK_ERROR;
1501 break;
1502 }
1503 RecycleTree(pn2, tc);
1504 } else {
1505 PN_APPEND(pn, pn2);
1506 }
1507 }
1508
1509 /*
1510 * Handle the case where there was a let declaration under this block. If
1511 * it replaced tc->blockNode with a new block node then we must refresh pn
1512 * and then restore tc->blockNode.
1513 */
1514 if (tc->blockNode != pn)
1515 pn = tc->blockNode;
1516 tc->blockNode = saveBlock;
1517
1518 ts->flags &= ~TSF_OPERAND;
1519 if (tt == TOK_ERROR)
1520 return NULL;
1521
1522 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1523 return pn;
1524 }
1525
1526 static JSParseNode *
Condition(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1527 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1528 {
1529 JSParseNode *pn, *pn2;
1530
1531 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1532 pn = Expr(cx, ts, tc);
1533 if (!pn)
1534 return NULL;
1535 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1536
1537 /*
1538 * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1539 * greater precedence than ==.
1540 * XXX not ECMA, but documented in several books -- now a strict warning.
1541 */
1542 if (pn->pn_type == TOK_ASSIGN &&
1543 pn->pn_op == JSOP_NOP &&
1544 pn->pn_right->pn_type > TOK_EQOP)
1545 {
1546 JSBool rewrite = !JS_VERSION_IS_ECMA(cx);
1547 if (!js_ReportCompileErrorNumber(cx, ts,
1548 JSREPORT_TS |
1549 JSREPORT_WARNING |
1550 JSREPORT_STRICT,
1551 JSMSG_EQUAL_AS_ASSIGN,
1552 rewrite
1553 ? "\nAssuming equality test"
1554 : "")) {
1555 return NULL;
1556 }
1557 if (rewrite) {
1558 pn->pn_type = TOK_EQOP;
1559 pn->pn_op = (JSOp)cx->jsop_eq;
1560 pn2 = pn->pn_left;
1561 switch (pn2->pn_op) {
1562 case JSOP_SETNAME:
1563 pn2->pn_op = JSOP_NAME;
1564 break;
1565 case JSOP_SETPROP:
1566 pn2->pn_op = JSOP_GETPROP;
1567 break;
1568 case JSOP_SETELEM:
1569 pn2->pn_op = JSOP_GETELEM;
1570 break;
1571 default:
1572 JS_ASSERT(0);
1573 }
1574 }
1575 }
1576 return pn;
1577 }
1578
1579 static JSBool
MatchLabel(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)1580 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1581 {
1582 JSAtom *label;
1583 JSTokenType tt;
1584
1585 tt = js_PeekTokenSameLine(cx, ts);
1586 if (tt == TOK_ERROR)
1587 return JS_FALSE;
1588 if (tt == TOK_NAME) {
1589 (void) js_GetToken(cx, ts);
1590 label = CURRENT_TOKEN(ts).t_atom;
1591 } else {
1592 label = NULL;
1593 }
1594 pn->pn_atom = label;
1595 return JS_TRUE;
1596 }
1597
1598 #if JS_HAS_EXPORT_IMPORT
1599 static JSParseNode *
ImportExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1600 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1601 {
1602 JSParseNode *pn, *pn2;
1603 JSTokenType tt;
1604
1605 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1606 pn = NewParseNode(cx, ts, PN_NAME, tc);
1607 if (!pn)
1608 return NULL;
1609 pn->pn_op = JSOP_NAME;
1610 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1611 pn->pn_expr = NULL;
1612 pn->pn_slot = -1;
1613 pn->pn_attrs = 0;
1614
1615 ts->flags |= TSF_OPERAND;
1616 while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1617 ts->flags &= ~TSF_OPERAND;
1618 if (pn->pn_op == JSOP_IMPORTALL)
1619 goto bad_import;
1620
1621 if (tt == TOK_DOT) {
1622 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1623 if (!pn2)
1624 return NULL;
1625 ts->flags |= TSF_KEYWORD_IS_NAME;
1626 if (js_MatchToken(cx, ts, TOK_STAR)) {
1627 pn2->pn_op = JSOP_IMPORTALL;
1628 pn2->pn_atom = NULL;
1629 pn2->pn_slot = -1;
1630 pn2->pn_attrs = 0;
1631 } else {
1632 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1633 pn2->pn_op = JSOP_GETPROP;
1634 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1635 pn2->pn_slot = -1;
1636 pn2->pn_attrs = 0;
1637 }
1638 ts->flags &= ~TSF_KEYWORD_IS_NAME;
1639 pn2->pn_expr = pn;
1640 pn2->pn_pos.begin = pn->pn_pos.begin;
1641 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1642 } else {
1643 /* Make a TOK_LB binary node. */
1644 pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc);
1645 if (!pn2)
1646 return NULL;
1647
1648 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1649 }
1650
1651 pn = pn2;
1652 ts->flags |= TSF_OPERAND;
1653 }
1654 ts->flags &= ~TSF_OPERAND;
1655 if (tt == TOK_ERROR)
1656 return NULL;
1657 js_UngetToken(ts);
1658
1659 switch (pn->pn_op) {
1660 case JSOP_GETPROP:
1661 pn->pn_op = JSOP_IMPORTPROP;
1662 break;
1663 case JSOP_GETELEM:
1664 pn->pn_op = JSOP_IMPORTELEM;
1665 break;
1666 case JSOP_IMPORTALL:
1667 break;
1668 default:
1669 goto bad_import;
1670 }
1671 return pn;
1672
1673 bad_import:
1674 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1675 JSMSG_BAD_IMPORT);
1676 return NULL;
1677 }
1678 #endif /* JS_HAS_EXPORT_IMPORT */
1679
1680 static JSBool
BindLet(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)1681 BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1682 {
1683 JSObject *blockObj;
1684 JSScopeProperty *sprop;
1685 JSAtomListElement *ale;
1686
1687 blockObj = data->obj;
1688 sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom));
1689 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1690 if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) {
1691 const char *name;
1692
1693 if (sprop) {
1694 JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1695 JS_ASSERT((uint16)sprop->shortid < data->u.let.index);
1696 }
1697
1698 name = js_AtomToPrintableString(cx, atom);
1699 if (name) {
1700 js_ReportCompileErrorNumber(cx,
1701 BIND_DATA_REPORT_ARGS(data,
1702 JSREPORT_ERROR),
1703 JSMSG_REDECLARED_VAR,
1704 (ale && ALE_JSOP(ale) == JSOP_DEFCONST)
1705 ? js_const_str
1706 : "variable",
1707 name);
1708 }
1709 return JS_FALSE;
1710 }
1711
1712 if (data->u.let.index == JS_BIT(16)) {
1713 js_ReportCompileErrorNumber(cx,
1714 BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR),
1715 data->u.let.overflow);
1716 return JS_FALSE;
1717 }
1718
1719 /* Use JSPROP_ENUMERATE to aid the disassembler. */
1720 return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom),
1721 JSVAL_VOID, NULL, NULL,
1722 JSPROP_ENUMERATE | JSPROP_PERMANENT,
1723 SPROP_HAS_SHORTID,
1724 (intN)data->u.let.index++,
1725 NULL);
1726 }
1727
1728 static JSBool
BindVarOrConst(JSContext * cx,BindData * data,JSAtom * atom,JSTreeContext * tc)1729 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
1730 {
1731 JSStmtInfo *stmt;
1732 JSAtomListElement *ale;
1733 JSOp op, prevop;
1734 const char *name;
1735 JSFunction *fun;
1736 JSObject *obj, *pobj;
1737 JSProperty *prop;
1738 JSBool ok;
1739 JSPropertyOp getter, setter;
1740 JSScopeProperty *sprop;
1741
1742 stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE);
1743 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1744 op = data->op;
1745 if ((stmt && stmt->type != STMT_WITH) || ale) {
1746 prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR;
1747 if (JS_HAS_STRICT_OPTION(cx)
1748 ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
1749 : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
1750 name = js_AtomToPrintableString(cx, atom);
1751 if (!name ||
1752 !js_ReportCompileErrorNumber(cx,
1753 BIND_DATA_REPORT_ARGS(data,
1754 (op != JSOP_DEFCONST &&
1755 prevop != JSOP_DEFCONST)
1756 ? JSREPORT_WARNING |
1757 JSREPORT_STRICT
1758 : JSREPORT_ERROR),
1759 JSMSG_REDECLARED_VAR,
1760 (prevop == JSOP_DEFFUN ||
1761 prevop == JSOP_CLOSURE)
1762 ? js_function_str
1763 : (prevop == JSOP_DEFCONST)
1764 ? js_const_str
1765 : js_var_str,
1766 name)) {
1767 return JS_FALSE;
1768 }
1769 }
1770 if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
1771 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1772 }
1773 if (!ale) {
1774 ale = js_IndexAtom(cx, atom, &tc->decls);
1775 if (!ale)
1776 return JS_FALSE;
1777 }
1778 ALE_SET_JSOP(ale, op);
1779
1780 fun = data->u.var.fun;
1781 obj = data->obj;
1782 if (!fun) {
1783 /* Don't lookup global variables at compile time. */
1784 prop = NULL;
1785 } else {
1786 JS_ASSERT(OBJ_IS_NATIVE(obj));
1787 if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
1788 &pobj, &prop)) {
1789 return JS_FALSE;
1790 }
1791 }
1792
1793 ok = JS_TRUE;
1794 getter = data->u.var.getter;
1795 setter = data->u.var.setter;
1796
1797 if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
1798 sprop = (JSScopeProperty *)prop;
1799 if (sprop->getter == js_GetArgument) {
1800 name = js_AtomToPrintableString(cx, atom);
1801 if (!name) {
1802 ok = JS_FALSE;
1803 } else if (op == JSOP_DEFCONST) {
1804 js_ReportCompileErrorNumber(cx,
1805 BIND_DATA_REPORT_ARGS(data,
1806 JSREPORT_ERROR),
1807 JSMSG_REDECLARED_PARAM,
1808 name);
1809 ok = JS_FALSE;
1810 } else {
1811 getter = js_GetArgument;
1812 setter = js_SetArgument;
1813 ok = js_ReportCompileErrorNumber(cx,
1814 BIND_DATA_REPORT_ARGS(data,
1815 JSREPORT_WARNING |
1816 JSREPORT_STRICT),
1817 JSMSG_VAR_HIDES_ARG,
1818 name);
1819 }
1820 } else {
1821 JS_ASSERT(getter == js_GetLocalVariable);
1822
1823 if (fun) {
1824 /* Not an argument, must be a redeclared local var. */
1825 if (data->u.var.clasp == &js_FunctionClass) {
1826 JS_ASSERT(sprop->getter == js_GetLocalVariable);
1827 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
1828 (uint16) sprop->shortid < fun->u.i.nvars);
1829 } else if (data->u.var.clasp == &js_CallClass) {
1830 if (sprop->getter == js_GetCallVariable) {
1831 /*
1832 * Referencing a name introduced by a var statement in
1833 * the enclosing function. Check that the slot number
1834 * we have is in range.
1835 */
1836 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
1837 (uint16) sprop->shortid < fun->u.i.nvars);
1838 } else {
1839 /*
1840 * A variable introduced through another eval: don't
1841 * use the special getters and setters since we can't
1842 * allocate a slot in the frame.
1843 */
1844 getter = sprop->getter;
1845 setter = sprop->setter;
1846 }
1847 }
1848
1849 /* Override the old getter and setter, to handle eval. */
1850 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
1851 0, sprop->attrs,
1852 getter, setter);
1853 if (!sprop)
1854 ok = JS_FALSE;
1855 }
1856 }
1857 if (prop)
1858 OBJ_DROP_PROPERTY(cx, pobj, prop);
1859 } else {
1860 /*
1861 * Property not found in current variable scope: we have not seen this
1862 * variable before. Define a new local variable by adding a property
1863 * to the function's scope, allocating one slot in the function's vars
1864 * frame. Global variables and any locals declared in with statement
1865 * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes
1866 * generated for slot-less vars.
1867 */
1868 sprop = NULL;
1869 if (prop) {
1870 OBJ_DROP_PROPERTY(cx, pobj, prop);
1871 prop = NULL;
1872 }
1873
1874 if (cx->fp->scopeChain == obj &&
1875 !js_InWithStatement(tc) &&
1876 !BindLocalVariable(cx, data, atom)) {
1877 return JS_FALSE;
1878 }
1879 }
1880 return ok;
1881 }
1882
1883 #if JS_HAS_DESTRUCTURING
1884
1885 static JSBool
BindDestructuringVar(JSContext * cx,BindData * data,JSParseNode * pn,JSTreeContext * tc)1886 BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn,
1887 JSTreeContext *tc)
1888 {
1889 JSAtom *atom;
1890
1891 /*
1892 * Destructuring is a form of assignment, so just as for an initialized
1893 * simple variable, we must check for assignment to 'arguments' and flag
1894 * the enclosing function (if any) as heavyweight.
1895 */
1896 JS_ASSERT(pn->pn_type == TOK_NAME);
1897 atom = pn->pn_atom;
1898 if (atom == cx->runtime->atomState.argumentsAtom)
1899 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1900
1901 data->pn = pn;
1902 if (!data->binder(cx, data, atom, tc))
1903 return JS_FALSE;
1904 data->pn = NULL;
1905
1906 /*
1907 * Select the appropriate name-setting opcode, which may be specialized
1908 * further for local variable and argument slot optimizations. At this
1909 * point, we can't select the optimal final opcode, yet we must preserve
1910 * the CONST bit and convey "set", not "get".
1911 */
1912 pn->pn_op = (data->op == JSOP_DEFCONST)
1913 ? JSOP_SETCONST
1914 : JSOP_SETNAME;
1915 pn->pn_attrs = data->u.var.attrs;
1916 return JS_TRUE;
1917 }
1918
1919 /*
1920 * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
1921 * LHS expression except a destructuring initialiser, and R is on the stack.
1922 * Because R is already evaluated, the usual LHS-specialized bytecodes won't
1923 * work. After pushing R[P] we need to evaluate Q's "reference base" QB and
1924 * then push its property name QN. At this point the stack looks like
1925 *
1926 * [... R, R[P], QB, QN]
1927 *
1928 * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
1929 * its operands with left-hand side above right-hand side:
1930 *
1931 * [rval, lval, xval]
1932 *
1933 * and pops all three values, setting lval[xval] = rval. But we cannot select
1934 * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
1935 * which can be optimized further. So we select JSOP_SETNAME.
1936 */
1937 static JSBool
BindDestructuringLHS(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)1938 BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
1939 {
1940 while (pn->pn_type == TOK_RP)
1941 pn = pn->pn_kid;
1942
1943 switch (pn->pn_type) {
1944 case TOK_NAME:
1945 if (pn->pn_atom == cx->runtime->atomState.argumentsAtom)
1946 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1947 /* FALL THROUGH */
1948 case TOK_DOT:
1949 case TOK_LB:
1950 pn->pn_op = JSOP_SETNAME;
1951 break;
1952
1953 #if JS_HAS_LVALUE_RETURN
1954 case TOK_LP:
1955 JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL);
1956 pn->pn_op = JSOP_SETCALL;
1957 break;
1958 #endif
1959
1960 #if JS_HAS_XML_SUPPORT
1961 case TOK_UNARYOP:
1962 if (pn->pn_op == JSOP_XMLNAME) {
1963 pn->pn_op = JSOP_BINDXMLNAME;
1964 break;
1965 }
1966 /* FALL THROUGH */
1967 #endif
1968
1969 default:
1970 js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
1971 JSMSG_BAD_LEFTSIDE_OF_ASS);
1972 return JS_FALSE;
1973 }
1974
1975 return JS_TRUE;
1976 }
1977
1978 typedef struct FindPropValData {
1979 uint32 numvars; /* # of destructuring vars in left side */
1980 uint32 maxstep; /* max # of steps searching right side */
1981 JSDHashTable table; /* hash table for O(1) right side search */
1982 } FindPropValData;
1983
1984 typedef struct FindPropValEntry {
1985 JSDHashEntryHdr hdr;
1986 JSParseNode *pnkey;
1987 JSParseNode *pnval;
1988 } FindPropValEntry;
1989
1990 #define ASSERT_VALID_PROPERTY_KEY(pnkey) \
1991 JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \
1992 ((pnkey)->pn_type == TOK_NUMBER || \
1993 (pnkey)->pn_type == TOK_STRING || \
1994 (pnkey)->pn_type == TOK_NAME))
1995
1996 JS_STATIC_DLL_CALLBACK(JSDHashNumber)
HashFindPropValKey(JSDHashTable * table,const void * key)1997 HashFindPropValKey(JSDHashTable *table, const void *key)
1998 {
1999 const JSParseNode *pnkey = (const JSParseNode *)key;
2000
2001 ASSERT_VALID_PROPERTY_KEY(pnkey);
2002 return (pnkey->pn_type == TOK_NUMBER)
2003 ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^
2004 JSDOUBLE_LO32(pnkey->pn_dval))
2005 : (JSDHashNumber) pnkey->pn_atom->number;
2006 }
2007
2008 JS_STATIC_DLL_CALLBACK(JSBool)
MatchFindPropValEntry(JSDHashTable * table,const JSDHashEntryHdr * entry,const void * key)2009 MatchFindPropValEntry(JSDHashTable *table,
2010 const JSDHashEntryHdr *entry,
2011 const void *key)
2012 {
2013 const FindPropValEntry *fpve = (const FindPropValEntry *)entry;
2014 const JSParseNode *pnkey = (const JSParseNode *)key;
2015
2016 ASSERT_VALID_PROPERTY_KEY(pnkey);
2017 return pnkey->pn_type == fpve->pnkey->pn_type &&
2018 ((pnkey->pn_type == TOK_NUMBER)
2019 ? pnkey->pn_dval == fpve->pnkey->pn_dval
2020 : pnkey->pn_atom == fpve->pnkey->pn_atom);
2021 }
2022
2023 static const JSDHashTableOps FindPropValOps = {
2024 JS_DHashAllocTable,
2025 JS_DHashFreeTable,
2026 JS_DHashGetKeyStub,
2027 HashFindPropValKey,
2028 MatchFindPropValEntry,
2029 JS_DHashMoveEntryStub,
2030 JS_DHashClearEntryStub,
2031 JS_DHashFinalizeStub,
2032 NULL
2033 };
2034
2035 #define STEP_HASH_THRESHOLD 10
2036 #define BIG_DESTRUCTURING 5
2037 #define BIG_OBJECT_INIT 20
2038
2039 static JSParseNode *
FindPropertyValue(JSParseNode * pn,JSParseNode * pnid,FindPropValData * data)2040 FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data)
2041 {
2042 FindPropValEntry *entry;
2043 JSParseNode *pnhit, *pnprop, *pnkey;
2044 uint32 step;
2045
2046 /* If we have a hash table, use it as the sole source of truth. */
2047 if (data->table.ops) {
2048 entry = (FindPropValEntry *)
2049 JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP);
2050 return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL;
2051 }
2052
2053 /* If pn is not an object initialiser node, we can't do anything here. */
2054 if (pn->pn_type != TOK_RC)
2055 return NULL;
2056
2057 /*
2058 * We must search all the way through pn's list, to handle the case of an
2059 * id duplicated for two or more property initialisers.
2060 */
2061 pnhit = NULL;
2062 step = 0;
2063 ASSERT_VALID_PROPERTY_KEY(pnid);
2064 if (pnid->pn_type == TOK_NUMBER) {
2065 for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) {
2066 JS_ASSERT(pnprop->pn_type == TOK_COLON);
2067 if (pnprop->pn_op == JSOP_NOP) {
2068 pnkey = pnprop->pn_left;
2069 ASSERT_VALID_PROPERTY_KEY(pnkey);
2070 if (pnkey->pn_type == TOK_NUMBER &&
2071 pnkey->pn_dval == pnid->pn_dval) {
2072 pnhit = pnprop;
2073 }
2074 ++step;
2075 }
2076 }
2077 } else {
2078 for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) {
2079 JS_ASSERT(pnprop->pn_type == TOK_COLON);
2080 if (pnprop->pn_op == JSOP_NOP) {
2081 pnkey = pnprop->pn_left;
2082 ASSERT_VALID_PROPERTY_KEY(pnkey);
2083 if (pnkey->pn_type == pnid->pn_type &&
2084 pnkey->pn_atom == pnid->pn_atom) {
2085 pnhit = pnprop;
2086 }
2087 ++step;
2088 }
2089 }
2090 }
2091 if (!pnhit)
2092 return NULL;
2093
2094 /* Hit via full search -- see whether it's time to create the hash table. */
2095 JS_ASSERT(!data->table.ops);
2096 if (step > data->maxstep) {
2097 data->maxstep = step;
2098 if (step >= STEP_HASH_THRESHOLD &&
2099 data->numvars >= BIG_DESTRUCTURING &&
2100 pn->pn_count >= BIG_OBJECT_INIT &&
2101 JS_DHashTableInit(&data->table, &FindPropValOps, pn,
2102 sizeof(FindPropValEntry), pn->pn_count)) {
2103
2104 for (pn = pn->pn_head; pn; pn = pn->pn_next) {
2105 ASSERT_VALID_PROPERTY_KEY(pn->pn_left);
2106 entry = (FindPropValEntry *)
2107 JS_DHashTableOperate(&data->table, pn->pn_left,
2108 JS_DHASH_ADD);
2109 entry->pnval = pn->pn_right;
2110 }
2111 }
2112 }
2113 return pnhit->pn_right;
2114 }
2115
2116 /*
2117 * If data is null, the caller is AssignExpr and instead of binding variables,
2118 * we specialize lvalues in the propery value positions of the left-hand side.
2119 * If right is null, just check for well-formed lvalues.
2120 */
2121 static JSBool
CheckDestructuring(JSContext * cx,BindData * data,JSParseNode * left,JSParseNode * right,JSTreeContext * tc)2122 CheckDestructuring(JSContext *cx, BindData *data,
2123 JSParseNode *left, JSParseNode *right,
2124 JSTreeContext *tc)
2125 {
2126 JSBool ok;
2127 FindPropValData fpvd;
2128 JSParseNode *lhs, *rhs, *pn, *pn2;
2129
2130 if (left->pn_type == TOK_ARRAYCOMP) {
2131 js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR,
2132 JSMSG_ARRAY_COMP_LEFTSIDE);
2133 return JS_FALSE;
2134 }
2135
2136 ok = JS_TRUE;
2137 fpvd.table.ops = NULL;
2138 lhs = left->pn_head;
2139 if (lhs && lhs->pn_type == TOK_DEFSHARP) {
2140 pn = lhs;
2141 goto no_var_name;
2142 }
2143
2144 if (left->pn_type == TOK_RB) {
2145 rhs = (right && right->pn_type == left->pn_type)
2146 ? right->pn_head
2147 : NULL;
2148
2149 while (lhs) {
2150 pn = lhs, pn2 = rhs;
2151 if (!data) {
2152 /* Skip parenthesization if not in a variable declaration. */
2153 while (pn->pn_type == TOK_RP)
2154 pn = pn->pn_kid;
2155 if (pn2) {
2156 while (pn2->pn_type == TOK_RP)
2157 pn2 = pn2->pn_kid;
2158 }
2159 }
2160
2161 /* Nullary comma is an elision; binary comma is an expression.*/
2162 if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) {
2163 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2164 ok = CheckDestructuring(cx, data, pn, pn2, tc);
2165 } else {
2166 if (data) {
2167 if (pn->pn_type != TOK_NAME)
2168 goto no_var_name;
2169
2170 ok = BindDestructuringVar(cx, data, pn, tc);
2171 } else {
2172 ok = BindDestructuringLHS(cx, pn, tc);
2173 }
2174 }
2175 if (!ok)
2176 goto out;
2177 }
2178
2179 lhs = lhs->pn_next;
2180 if (rhs)
2181 rhs = rhs->pn_next;
2182 }
2183 } else {
2184 JS_ASSERT(left->pn_type == TOK_RC);
2185 fpvd.numvars = left->pn_count;
2186 fpvd.maxstep = 0;
2187 rhs = NULL;
2188
2189 while (lhs) {
2190 JS_ASSERT(lhs->pn_type == TOK_COLON);
2191 pn = lhs->pn_right;
2192 if (!data) {
2193 /* Skip parenthesization if not in a variable declaration. */
2194 while (pn->pn_type == TOK_RP)
2195 pn = pn->pn_kid;
2196 }
2197
2198 if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
2199 if (right) {
2200 rhs = FindPropertyValue(right, lhs->pn_left, &fpvd);
2201 if (rhs && !data) {
2202 while (rhs->pn_type == TOK_RP)
2203 rhs = rhs->pn_kid;
2204 }
2205 }
2206
2207 ok = CheckDestructuring(cx, data, pn, rhs, tc);
2208 } else if (data) {
2209 if (pn->pn_type != TOK_NAME)
2210 goto no_var_name;
2211
2212 ok = BindDestructuringVar(cx, data, pn, tc);
2213 } else {
2214 ok = BindDestructuringLHS(cx, pn, tc);
2215 }
2216 if (!ok)
2217 goto out;
2218
2219 lhs = lhs->pn_next;
2220 }
2221 }
2222
2223 out:
2224 if (fpvd.table.ops)
2225 JS_DHashTableFinish(&fpvd.table);
2226 return ok;
2227
2228 no_var_name:
2229 js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR,
2230 JSMSG_NO_VARIABLE_NAME);
2231 ok = JS_FALSE;
2232 goto out;
2233 }
2234
2235 static JSParseNode *
DestructuringExpr(JSContext * cx,BindData * data,JSTreeContext * tc,JSTokenType tt)2236 DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc,
2237 JSTokenType tt)
2238 {
2239 JSParseNode *pn;
2240
2241 pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE);
2242 if (!pn)
2243 return NULL;
2244 if (!CheckDestructuring(cx, data, pn, NULL, tc))
2245 return NULL;
2246 return pn;
2247 }
2248
2249 #endif /* JS_HAS_DESTRUCTURING */
2250
2251 extern const char js_with_statement_str[];
2252
2253 static JSParseNode *
ContainsStmt(JSParseNode * pn,JSTokenType tt)2254 ContainsStmt(JSParseNode *pn, JSTokenType tt)
2255 {
2256 JSParseNode *pn2, *pnt;
2257
2258 if (!pn)
2259 return NULL;
2260 if (pn->pn_type == tt)
2261 return pn;
2262 switch (pn->pn_arity) {
2263 case PN_LIST:
2264 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2265 pnt = ContainsStmt(pn2, tt);
2266 if (pnt)
2267 return pnt;
2268 }
2269 break;
2270 case PN_TERNARY:
2271 pnt = ContainsStmt(pn->pn_kid1, tt);
2272 if (pnt)
2273 return pnt;
2274 pnt = ContainsStmt(pn->pn_kid2, tt);
2275 if (pnt)
2276 return pnt;
2277 return ContainsStmt(pn->pn_kid3, tt);
2278 case PN_BINARY:
2279 /*
2280 * Limit recursion if pn is a binary expression, which can't contain a
2281 * var statement.
2282 */
2283 if (pn->pn_op != JSOP_NOP)
2284 return NULL;
2285 pnt = ContainsStmt(pn->pn_left, tt);
2286 if (pnt)
2287 return pnt;
2288 return ContainsStmt(pn->pn_right, tt);
2289 case PN_UNARY:
2290 if (pn->pn_op != JSOP_NOP)
2291 return NULL;
2292 return ContainsStmt(pn->pn_kid, tt);
2293 case PN_NAME:
2294 return ContainsStmt(pn->pn_expr, tt);
2295 default:;
2296 }
2297 return NULL;
2298 }
2299
2300 static JSParseNode *
ReturnOrYield(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParser operandParser)2301 ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2302 JSParser operandParser)
2303 {
2304 JSTokenType tt, tt2;
2305 JSParseNode *pn, *pn2;
2306
2307 tt = CURRENT_TOKEN(ts).type;
2308 if (!(tc->flags & TCF_IN_FUNCTION)) {
2309 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2310 JSMSG_BAD_RETURN_OR_YIELD,
2311 #if JS_HAS_GENERATORS
2312 (tt == TOK_YIELD) ? js_yield_str :
2313 #endif
2314 js_return_str);
2315 return NULL;
2316 }
2317
2318 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2319 if (!pn)
2320 return NULL;
2321
2322 #if JS_HAS_GENERATORS
2323 if (tt == TOK_YIELD)
2324 tc->flags |= TCF_FUN_IS_GENERATOR;
2325 #endif
2326
2327 /* This is ugly, but we don't want to require a semicolon. */
2328 ts->flags |= TSF_OPERAND;
2329 tt2 = js_PeekTokenSameLine(cx, ts);
2330 ts->flags &= ~TSF_OPERAND;
2331 if (tt2 == TOK_ERROR)
2332 return NULL;
2333
2334 if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC
2335 #if JS_HAS_GENERATORS
2336 && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP))
2337 #endif
2338 ) {
2339 pn2 = operandParser(cx, ts, tc);
2340 if (!pn2)
2341 return NULL;
2342 #if JS_HAS_GENERATORS
2343 if (tt == TOK_RETURN)
2344 #endif
2345 tc->flags |= TCF_RETURN_EXPR;
2346 pn->pn_pos.end = pn2->pn_pos.end;
2347 pn->pn_kid = pn2;
2348 } else {
2349 #if JS_HAS_GENERATORS
2350 if (tt == TOK_RETURN)
2351 #endif
2352 tc->flags |= TCF_RETURN_VOID;
2353 pn->pn_kid = NULL;
2354 }
2355
2356 if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
2357 /* As in Python (see PEP-255), disallow return v; in generators. */
2358 ReportBadReturn(cx, ts, JSREPORT_ERROR,
2359 JSMSG_BAD_GENERATOR_RETURN,
2360 JSMSG_BAD_ANON_GENERATOR_RETURN);
2361 return NULL;
2362 }
2363
2364 if (JS_HAS_STRICT_OPTION(cx) &&
2365 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
2366 !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
2367 JSMSG_NO_RETURN_VALUE,
2368 JSMSG_ANON_NO_RETURN_VALUE)) {
2369 return NULL;
2370 }
2371
2372 return pn;
2373 }
2374
2375 static JSParseNode *
PushLexicalScope(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSStmtInfo * stmtInfo)2376 PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2377 JSStmtInfo *stmtInfo)
2378 {
2379 JSParseNode *pn;
2380 JSObject *obj;
2381 JSAtom *atom;
2382
2383 pn = NewParseNode(cx, ts, PN_NAME, tc);
2384 if (!pn)
2385 return NULL;
2386
2387 obj = js_NewBlockObject(cx);
2388 if (!obj)
2389 return NULL;
2390
2391 atom = js_AtomizeObject(cx, obj, 0);
2392 if (!atom)
2393 return NULL;
2394
2395 js_PushBlockScope(tc, stmtInfo, atom, -1);
2396 pn->pn_type = TOK_LEXICALSCOPE;
2397 pn->pn_op = JSOP_LEAVEBLOCK;
2398 pn->pn_atom = atom;
2399 pn->pn_expr = NULL;
2400 pn->pn_slot = -1;
2401 pn->pn_attrs = 0;
2402 return pn;
2403 }
2404
2405 #if JS_HAS_BLOCK_SCOPE
2406
2407 static JSParseNode *
LetBlock(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool statement)2408 LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement)
2409 {
2410 JSParseNode *pn, *pnblock, *pnlet;
2411 JSStmtInfo stmtInfo;
2412
2413 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET);
2414
2415 /* Create the let binary node. */
2416 pnlet = NewParseNode(cx, ts, PN_BINARY, tc);
2417 if (!pnlet)
2418 return NULL;
2419
2420 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET);
2421
2422 /* This is a let block or expression of the form: let (a, b, c) .... */
2423 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
2424 if (!pnblock)
2425 return NULL;
2426 pn = pnblock;
2427 pn->pn_expr = pnlet;
2428
2429 pnlet->pn_left = Variables(cx, ts, tc);
2430 if (!pnlet->pn_left)
2431 return NULL;
2432 pnlet->pn_left->pn_extra = PNX_POPVAR;
2433
2434 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
2435
2436 ts->flags |= TSF_OPERAND;
2437 if (statement && !js_MatchToken(cx, ts, TOK_LC)) {
2438 /*
2439 * If this is really an expression in let statement guise, then we
2440 * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
2441 * the return value of the expression.
2442 */
2443 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2444 if (!pn)
2445 return NULL;
2446 pn->pn_type = TOK_SEMI;
2447 pn->pn_num = -1;
2448 pn->pn_kid = pnblock;
2449
2450 statement = JS_FALSE;
2451 }
2452 ts->flags &= ~TSF_OPERAND;
2453
2454 if (statement) {
2455 pnlet->pn_right = Statements(cx, ts, tc);
2456 if (!pnlet->pn_right)
2457 return NULL;
2458 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
2459 } else {
2460 /*
2461 * Change pnblock's opcode to the variant that propagates the last
2462 * result down after popping the block, and clear statement.
2463 */
2464 pnblock->pn_op = JSOP_LEAVEBLOCKEXPR;
2465 pnlet->pn_right = Expr(cx, ts, tc);
2466 if (!pnlet->pn_right)
2467 return NULL;
2468 }
2469
2470 js_PopStatement(tc);
2471 return pn;
2472 }
2473
2474 #endif /* JS_HAS_BLOCK_SCOPE */
2475
2476 static JSParseNode *
Statement(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)2477 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2478 {
2479 JSTokenType tt;
2480 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
2481 JSStmtInfo stmtInfo, *stmt, *stmt2;
2482 JSAtom *label;
2483
2484 CHECK_RECURSION();
2485
2486 ts->flags |= TSF_OPERAND;
2487 tt = js_GetToken(cx, ts);
2488 ts->flags &= ~TSF_OPERAND;
2489
2490 #if JS_HAS_GETTER_SETTER
2491 if (tt == TOK_NAME) {
2492 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2493 if (tt == TOK_ERROR)
2494 return NULL;
2495 }
2496 #endif
2497
2498 switch (tt) {
2499 #if JS_HAS_EXPORT_IMPORT
2500 case TOK_EXPORT:
2501 pn = NewParseNode(cx, ts, PN_LIST, tc);
2502 if (!pn)
2503 return NULL;
2504 PN_INIT_LIST(pn);
2505 if (js_MatchToken(cx, ts, TOK_STAR)) {
2506 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
2507 if (!pn2)
2508 return NULL;
2509 PN_APPEND(pn, pn2);
2510 } else {
2511 do {
2512 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
2513 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2514 if (!pn2)
2515 return NULL;
2516 pn2->pn_op = JSOP_NAME;
2517 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2518 pn2->pn_expr = NULL;
2519 pn2->pn_slot = -1;
2520 pn2->pn_attrs = 0;
2521 PN_APPEND(pn, pn2);
2522 } while (js_MatchToken(cx, ts, TOK_COMMA));
2523 }
2524 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2525 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2526 break;
2527
2528 case TOK_IMPORT:
2529 pn = NewParseNode(cx, ts, PN_LIST, tc);
2530 if (!pn)
2531 return NULL;
2532 PN_INIT_LIST(pn);
2533 do {
2534 pn2 = ImportExpr(cx, ts, tc);
2535 if (!pn2)
2536 return NULL;
2537 PN_APPEND(pn, pn2);
2538 } while (js_MatchToken(cx, ts, TOK_COMMA));
2539 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2540 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2541 break;
2542 #endif /* JS_HAS_EXPORT_IMPORT */
2543
2544 case TOK_FUNCTION:
2545 #if JS_HAS_XML_SUPPORT
2546 ts->flags |= TSF_KEYWORD_IS_NAME;
2547 tt = js_PeekToken(cx, ts);
2548 ts->flags &= ~TSF_KEYWORD_IS_NAME;
2549 if (tt == TOK_DBLCOLON)
2550 goto expression;
2551 #endif
2552 return FunctionStmt(cx, ts, tc);
2553
2554 case TOK_IF:
2555 /* An IF node has three kids: condition, then, and optional else. */
2556 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2557 if (!pn)
2558 return NULL;
2559 pn1 = Condition(cx, ts, tc);
2560 if (!pn1)
2561 return NULL;
2562 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
2563 pn2 = Statement(cx, ts, tc);
2564 if (!pn2)
2565 return NULL;
2566 ts->flags |= TSF_OPERAND;
2567 if (js_MatchToken(cx, ts, TOK_ELSE)) {
2568 ts->flags &= ~TSF_OPERAND;
2569 stmtInfo.type = STMT_ELSE;
2570 pn3 = Statement(cx, ts, tc);
2571 if (!pn3)
2572 return NULL;
2573 pn->pn_pos.end = pn3->pn_pos.end;
2574 } else {
2575 ts->flags &= ~TSF_OPERAND;
2576 pn3 = NULL;
2577 pn->pn_pos.end = pn2->pn_pos.end;
2578 }
2579 js_PopStatement(tc);
2580 pn->pn_kid1 = pn1;
2581 pn->pn_kid2 = pn2;
2582 pn->pn_kid3 = pn3;
2583 return pn;
2584
2585 case TOK_SWITCH:
2586 {
2587 JSParseNode *pn5, *saveBlock;
2588 JSBool seenDefault = JS_FALSE;
2589
2590 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2591 if (!pn)
2592 return NULL;
2593 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
2594
2595 /* pn1 points to the switch's discriminant. */
2596 pn1 = Expr(cx, ts, tc);
2597 if (!pn1)
2598 return NULL;
2599
2600 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
2601 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
2602
2603 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
2604 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2605 if (!pn2)
2606 return NULL;
2607 saveBlock = tc->blockNode;
2608 tc->blockNode = pn2;
2609 PN_INIT_LIST(pn2);
2610
2611 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
2612
2613 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
2614 switch (tt) {
2615 case TOK_DEFAULT:
2616 if (seenDefault) {
2617 js_ReportCompileErrorNumber(cx, ts,
2618 JSREPORT_TS | JSREPORT_ERROR,
2619 JSMSG_TOO_MANY_DEFAULTS);
2620 return NULL;
2621 }
2622 seenDefault = JS_TRUE;
2623 /* fall through */
2624
2625 case TOK_CASE:
2626 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
2627 if (!pn3)
2628 return NULL;
2629 if (tt == TOK_DEFAULT) {
2630 pn3->pn_left = NULL;
2631 } else {
2632 pn3->pn_left = Expr(cx, ts, tc);
2633 if (!pn3->pn_left)
2634 return NULL;
2635 }
2636 PN_APPEND(pn2, pn3);
2637 if (pn2->pn_count == JS_BIT(16)) {
2638 js_ReportCompileErrorNumber(cx, ts,
2639 JSREPORT_TS | JSREPORT_ERROR,
2640 JSMSG_TOO_MANY_CASES);
2641 return NULL;
2642 }
2643 break;
2644
2645 case TOK_ERROR:
2646 return NULL;
2647
2648 default:
2649 js_ReportCompileErrorNumber(cx, ts,
2650 JSREPORT_TS | JSREPORT_ERROR,
2651 JSMSG_BAD_SWITCH);
2652 return NULL;
2653 }
2654 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
2655
2656 pn4 = NewParseNode(cx, ts, PN_LIST, tc);
2657 if (!pn4)
2658 return NULL;
2659 pn4->pn_type = TOK_LC;
2660 PN_INIT_LIST(pn4);
2661 ts->flags |= TSF_OPERAND;
2662 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
2663 tt != TOK_CASE && tt != TOK_DEFAULT) {
2664 ts->flags &= ~TSF_OPERAND;
2665 if (tt == TOK_ERROR)
2666 return NULL;
2667 pn5 = Statement(cx, ts, tc);
2668 if (!pn5)
2669 return NULL;
2670 pn4->pn_pos.end = pn5->pn_pos.end;
2671 PN_APPEND(pn4, pn5);
2672 ts->flags |= TSF_OPERAND;
2673 }
2674 ts->flags &= ~TSF_OPERAND;
2675
2676 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
2677 if (pn4->pn_head)
2678 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
2679 pn3->pn_pos.end = pn4->pn_pos.end;
2680 pn3->pn_right = pn4;
2681 }
2682
2683 /*
2684 * Handle the case where there was a let declaration in any case in
2685 * the switch body, but not within an inner block. If it replaced
2686 * tc->blockNode with a new block node then we must refresh pn2 and
2687 * then restore tc->blockNode.
2688 */
2689 if (tc->blockNode != pn2)
2690 pn2 = tc->blockNode;
2691 tc->blockNode = saveBlock;
2692 js_PopStatement(tc);
2693
2694 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2695 pn->pn_left = pn1;
2696 pn->pn_right = pn2;
2697 return pn;
2698 }
2699
2700 case TOK_WHILE:
2701 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2702 if (!pn)
2703 return NULL;
2704 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
2705 pn2 = Condition(cx, ts, tc);
2706 if (!pn2)
2707 return NULL;
2708 pn->pn_left = pn2;
2709 pn2 = Statement(cx, ts, tc);
2710 if (!pn2)
2711 return NULL;
2712 js_PopStatement(tc);
2713 pn->pn_pos.end = pn2->pn_pos.end;
2714 pn->pn_right = pn2;
2715 return pn;
2716
2717 case TOK_DO:
2718 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2719 if (!pn)
2720 return NULL;
2721 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
2722 pn2 = Statement(cx, ts, tc);
2723 if (!pn2)
2724 return NULL;
2725 pn->pn_left = pn2;
2726 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
2727 pn2 = Condition(cx, ts, tc);
2728 if (!pn2)
2729 return NULL;
2730 js_PopStatement(tc);
2731 pn->pn_pos.end = pn2->pn_pos.end;
2732 pn->pn_right = pn2;
2733 if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) {
2734 /*
2735 * All legacy and extended versions must do automatic semicolon
2736 * insertion after do-while. See the testcase and discussion in
2737 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
2738 */
2739 (void) js_MatchToken(cx, ts, TOK_SEMI);
2740 return pn;
2741 }
2742 break;
2743
2744 case TOK_FOR:
2745 {
2746 #if JS_HAS_BLOCK_SCOPE
2747 JSParseNode *pnlet;
2748 JSStmtInfo blockInfo;
2749
2750 pnlet = NULL;
2751 #endif
2752
2753 /* A FOR node is binary, left is loop control and right is the body. */
2754 pn = NewParseNode(cx, ts, PN_BINARY, tc);
2755 if (!pn)
2756 return NULL;
2757 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
2758
2759 pn->pn_op = JSOP_FORIN;
2760 if (js_MatchToken(cx, ts, TOK_NAME)) {
2761 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
2762 pn->pn_op = JSOP_FOREACH;
2763 else
2764 js_UngetToken(ts);
2765 }
2766
2767 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
2768 ts->flags |= TSF_OPERAND;
2769 tt = js_PeekToken(cx, ts);
2770 ts->flags &= ~TSF_OPERAND;
2771 if (tt == TOK_SEMI) {
2772 if (pn->pn_op == JSOP_FOREACH)
2773 goto bad_for_each;
2774
2775 /* No initializer -- set first kid of left sub-node to null. */
2776 pn1 = NULL;
2777 } else {
2778 /*
2779 * Set pn1 to a var list or an initializing expression.
2780 *
2781 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
2782 * of the for statement. This flag will be used by the RelExpr
2783 * production; if it is set, then the 'in' keyword will not be
2784 * recognized as an operator, leaving it available to be parsed as
2785 * part of a for/in loop.
2786 *
2787 * A side effect of this restriction is that (unparenthesized)
2788 * expressions involving an 'in' operator are illegal in the init
2789 * clause of an ordinary for loop.
2790 */
2791 tc->flags |= TCF_IN_FOR_INIT;
2792 if (tt == TOK_VAR) {
2793 (void) js_GetToken(cx, ts);
2794 pn1 = Variables(cx, ts, tc);
2795 #if JS_HAS_BLOCK_SCOPE
2796 } else if (tt == TOK_LET) {
2797 (void) js_GetToken(cx, ts);
2798 if (js_PeekToken(cx, ts) == TOK_LP) {
2799 pn1 = LetBlock(cx, ts, tc, JS_FALSE);
2800 tt = TOK_LEXICALSCOPE;
2801 } else {
2802 pnlet = PushLexicalScope(cx, ts, tc, &blockInfo);
2803 if (!pnlet)
2804 return NULL;
2805 pn1 = Variables(cx, ts, tc);
2806 }
2807 #endif
2808 } else {
2809 pn1 = Expr(cx, ts, tc);
2810 if (pn1) {
2811 while (pn1->pn_type == TOK_RP)
2812 pn1 = pn1->pn_kid;
2813 }
2814 }
2815 tc->flags &= ~TCF_IN_FOR_INIT;
2816 if (!pn1)
2817 return NULL;
2818 }
2819
2820 /*
2821 * We can be sure that it's a for/in loop if there's still an 'in'
2822 * keyword here, even if JavaScript recognizes 'in' as an operator,
2823 * as we've excluded 'in' from being parsed in RelExpr by setting
2824 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
2825 */
2826 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
2827 stmtInfo.type = STMT_FOR_IN_LOOP;
2828
2829 /* Check that the left side of the 'in' is valid. */
2830 JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt);
2831 if (TOKEN_TYPE_IS_DECL(tt)
2832 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
2833 #if JS_HAS_DESTRUCTURING
2834 || (pn->pn_op == JSOP_FORIN &&
2835 (pn1->pn_head->pn_type == TOK_RC ||
2836 (pn1->pn_head->pn_type == TOK_RB &&
2837 pn1->pn_head->pn_count != 2) ||
2838 (pn1->pn_head->pn_type == TOK_ASSIGN &&
2839 (pn1->pn_head->pn_left->pn_type != TOK_RB ||
2840 pn1->pn_head->pn_left->pn_count != 2))))
2841 #endif
2842 )
2843 : (pn1->pn_type != TOK_NAME &&
2844 pn1->pn_type != TOK_DOT &&
2845 #if JS_HAS_DESTRUCTURING
2846 ((pn->pn_op == JSOP_FORIN)
2847 ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
2848 : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
2849 #endif
2850 #if JS_HAS_LVALUE_RETURN
2851 pn1->pn_type != TOK_LP &&
2852 #endif
2853 #if JS_HAS_XML_SUPPORT
2854 (pn1->pn_type != TOK_UNARYOP ||
2855 pn1->pn_op != JSOP_XMLNAME) &&
2856 #endif
2857 pn1->pn_type != TOK_LB)) {
2858 js_ReportCompileErrorNumber(cx, ts,
2859 JSREPORT_TS | JSREPORT_ERROR,
2860 JSMSG_BAD_FOR_LEFTSIDE);
2861 return NULL;
2862 }
2863
2864 if (TOKEN_TYPE_IS_DECL(tt)) {
2865 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
2866 pn1->pn_extra |= PNX_FORINVAR;
2867
2868 /*
2869 * Generate a final POP only if the variable is a simple name
2870 * (which means it is not a destructuring left-hand side) and
2871 * it has an initializer.
2872 */
2873 pn2 = pn1->pn_head;
2874 if (pn2->pn_type == TOK_NAME && pn2->pn_expr)
2875 pn1->pn_extra |= PNX_POPVAR;
2876 } else {
2877 pn2 = pn1;
2878 #if JS_HAS_LVALUE_RETURN
2879 if (pn2->pn_type == TOK_LP)
2880 pn2->pn_op = JSOP_SETCALL;
2881 #endif
2882 #if JS_HAS_XML_SUPPORT
2883 if (pn2->pn_type == TOK_UNARYOP)
2884 pn2->pn_op = JSOP_BINDXMLNAME;
2885 #endif
2886 }
2887
2888 switch (pn2->pn_type) {
2889 case TOK_NAME:
2890 /* Beware 'for (arguments in ...)' with or without a 'var'. */
2891 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2892 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2893 break;
2894
2895 #if JS_HAS_DESTRUCTURING
2896 case TOK_ASSIGN:
2897 pn2 = pn2->pn_left;
2898 JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC);
2899 /* FALL THROUGH */
2900 case TOK_RB:
2901 case TOK_RC:
2902 /* Check for valid lvalues in var-less destructuring for-in. */
2903 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
2904 return NULL;
2905
2906 /* Destructuring for-in requires [key, value] enumeration. */
2907 if (pn->pn_op != JSOP_FOREACH)
2908 pn->pn_op = JSOP_FOREACHKEYVAL;
2909 break;
2910 #endif
2911
2912 default:;
2913 }
2914
2915 /* Parse the object expression as the right operand of 'in'. */
2916 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
2917 if (!pn2)
2918 return NULL;
2919 pn->pn_left = pn2;
2920 } else {
2921 if (pn->pn_op == JSOP_FOREACH)
2922 goto bad_for_each;
2923 pn->pn_op = JSOP_NOP;
2924
2925 /* Parse the loop condition or null into pn2. */
2926 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
2927 ts->flags |= TSF_OPERAND;
2928 tt = js_PeekToken(cx, ts);
2929 ts->flags &= ~TSF_OPERAND;
2930 if (tt == TOK_SEMI) {
2931 pn2 = NULL;
2932 } else {
2933 pn2 = Expr(cx, ts, tc);
2934 if (!pn2)
2935 return NULL;
2936 }
2937
2938 /* Parse the update expression or null into pn3. */
2939 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
2940 ts->flags |= TSF_OPERAND;
2941 tt = js_PeekToken(cx, ts);
2942 ts->flags &= ~TSF_OPERAND;
2943 if (tt == TOK_RP) {
2944 pn3 = NULL;
2945 } else {
2946 pn3 = Expr(cx, ts, tc);
2947 if (!pn3)
2948 return NULL;
2949 }
2950
2951 /* Build the RESERVED node to use as the left kid of pn. */
2952 pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
2953 if (!pn4)
2954 return NULL;
2955 pn4->pn_type = TOK_RESERVED;
2956 pn4->pn_op = JSOP_NOP;
2957 pn4->pn_kid1 = pn1;
2958 pn4->pn_kid2 = pn2;
2959 pn4->pn_kid3 = pn3;
2960 pn->pn_left = pn4;
2961 }
2962
2963 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
2964
2965 /* Parse the loop body into pn->pn_right. */
2966 pn2 = Statement(cx, ts, tc);
2967 if (!pn2)
2968 return NULL;
2969 pn->pn_right = pn2;
2970
2971 /* Record the absolute line number for source note emission. */
2972 pn->pn_pos.end = pn2->pn_pos.end;
2973
2974 #if JS_HAS_BLOCK_SCOPE
2975 if (pnlet) {
2976 js_PopStatement(tc);
2977 pnlet->pn_expr = pn;
2978 pn = pnlet;
2979 }
2980 #endif
2981 js_PopStatement(tc);
2982 return pn;
2983
2984 bad_for_each:
2985 js_ReportCompileErrorNumber(cx, pn,
2986 JSREPORT_PN | JSREPORT_ERROR,
2987 JSMSG_BAD_FOR_EACH_LOOP);
2988 return NULL;
2989 }
2990
2991 case TOK_TRY: {
2992 JSParseNode *catchList, *lastCatch;
2993
2994 /*
2995 * try nodes are ternary.
2996 * kid1 is the try Statement
2997 * kid2 is the catch node list or null
2998 * kid3 is the finally Statement
2999 *
3000 * catch nodes are ternary.
3001 * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC)
3002 * kid2 is the catch guard or null if no guard
3003 * kid3 is the catch block
3004 *
3005 * catch lvalue nodes are either:
3006 * TOK_NAME for a single identifier
3007 * TOK_RB or TOK_RC for a destructuring left-hand side
3008 *
3009 * finally nodes are TOK_LC Statement lists.
3010 */
3011 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
3012 if (!pn)
3013 return NULL;
3014 pn->pn_op = JSOP_NOP;
3015
3016 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
3017 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
3018 pn->pn_kid1 = Statements(cx, ts, tc);
3019 if (!pn->pn_kid1)
3020 return NULL;
3021 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
3022 js_PopStatement(tc);
3023
3024 catchList = NULL;
3025 tt = js_GetToken(cx, ts);
3026 if (tt == TOK_CATCH) {
3027 catchList = NewParseNode(cx, ts, PN_LIST, tc);
3028 if (!catchList)
3029 return NULL;
3030 catchList->pn_type = TOK_RESERVED;
3031 PN_INIT_LIST(catchList);
3032 lastCatch = NULL;
3033
3034 do {
3035 JSParseNode *pnblock;
3036 BindData data;
3037
3038 /* Check for another catch after unconditional catch. */
3039 if (lastCatch && !lastCatch->pn_kid2) {
3040 js_ReportCompileErrorNumber(cx, ts,
3041 JSREPORT_TS | JSREPORT_ERROR,
3042 JSMSG_CATCH_AFTER_GENERAL);
3043 return NULL;
3044 }
3045
3046 /*
3047 * Create a lexical scope node around the whole catch clause,
3048 * including the head.
3049 */
3050 pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo);
3051 if (!pnblock)
3052 return NULL;
3053 stmtInfo.type = STMT_CATCH;
3054
3055 /*
3056 * Legal catch forms are:
3057 * catch (lhs)
3058 * catch (lhs if <boolean_expression>)
3059 * where lhs is a name or a destructuring left-hand side.
3060 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
3061 */
3062 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
3063 if (!pn2)
3064 return NULL;
3065 pnblock->pn_expr = pn2;
3066 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
3067
3068 /*
3069 * Contrary to ECMA Ed. 3, the catch variable is lexically
3070 * scoped, not a property of a new Object instance. This is
3071 * an intentional change that anticipates ECMA Ed. 4.
3072 */
3073 data.pn = NULL;
3074 data.ts = ts;
3075 data.obj = tc->blockChain;
3076 data.op = JSOP_NOP;
3077 data.binder = BindLet;
3078 data.u.let.index = 0;
3079 data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS;
3080
3081 tt = js_GetToken(cx, ts);
3082 switch (tt) {
3083 #if JS_HAS_DESTRUCTURING
3084 case TOK_LB:
3085 case TOK_LC:
3086 pn3 = DestructuringExpr(cx, &data, tc, tt);
3087 if (!pn3)
3088 return NULL;
3089 break;
3090 #endif
3091
3092 case TOK_NAME:
3093 label = CURRENT_TOKEN(ts).t_atom;
3094 if (!data.binder(cx, &data, label, tc))
3095 return NULL;
3096
3097 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3098 if (!pn3)
3099 return NULL;
3100 pn3->pn_atom = label;
3101 pn3->pn_expr = NULL;
3102 pn3->pn_slot = 0;
3103 pn3->pn_attrs = 0;
3104 break;
3105
3106 default:
3107 js_ReportCompileErrorNumber(cx, ts,
3108 JSREPORT_TS | JSREPORT_ERROR,
3109 JSMSG_CATCH_IDENTIFIER);
3110 return NULL;
3111 }
3112
3113 pn2->pn_kid1 = pn3;
3114 pn2->pn_kid2 = NULL;
3115 #if JS_HAS_CATCH_GUARD
3116 /*
3117 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
3118 * to avoid conflicting with the JS2/ECMAv4 type annotation
3119 * catchguard syntax.
3120 */
3121 if (js_MatchToken(cx, ts, TOK_IF)) {
3122 pn2->pn_kid2 = Expr(cx, ts, tc);
3123 if (!pn2->pn_kid2)
3124 return NULL;
3125 }
3126 #endif
3127 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
3128
3129 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
3130 pn2->pn_kid3 = Statements(cx, ts, tc);
3131 if (!pn2->pn_kid3)
3132 return NULL;
3133 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
3134 js_PopStatement(tc);
3135
3136 PN_APPEND(catchList, pnblock);
3137 lastCatch = pn2;
3138 ts->flags |= TSF_OPERAND;
3139 tt = js_GetToken(cx, ts);
3140 ts->flags &= ~TSF_OPERAND;
3141 } while (tt == TOK_CATCH);
3142 }
3143 pn->pn_kid2 = catchList;
3144
3145 if (tt == TOK_FINALLY) {
3146 tc->tryCount++;
3147 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
3148 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
3149 pn->pn_kid3 = Statements(cx, ts, tc);
3150 if (!pn->pn_kid3)
3151 return NULL;
3152 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
3153 js_PopStatement(tc);
3154 } else {
3155 js_UngetToken(ts);
3156 pn->pn_kid3 = NULL;
3157 }
3158 if (!catchList && !pn->pn_kid3) {
3159 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3160 JSMSG_CATCH_OR_FINALLY);
3161 return NULL;
3162 }
3163 tc->tryCount++;
3164 return pn;
3165 }
3166
3167 case TOK_THROW:
3168 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3169 if (!pn)
3170 return NULL;
3171
3172 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
3173 ts->flags |= TSF_OPERAND;
3174 tt = js_PeekTokenSameLine(cx, ts);
3175 ts->flags &= ~TSF_OPERAND;
3176 if (tt == TOK_ERROR)
3177 return NULL;
3178 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
3179 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3180 JSMSG_SYNTAX_ERROR);
3181 return NULL;
3182 }
3183
3184 pn2 = Expr(cx, ts, tc);
3185 if (!pn2)
3186 return NULL;
3187 pn->pn_pos.end = pn2->pn_pos.end;
3188 pn->pn_op = JSOP_THROW;
3189 pn->pn_kid = pn2;
3190 break;
3191
3192 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
3193 case TOK_CATCH:
3194 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3195 JSMSG_CATCH_WITHOUT_TRY);
3196 return NULL;
3197
3198 case TOK_FINALLY:
3199 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3200 JSMSG_FINALLY_WITHOUT_TRY);
3201 return NULL;
3202
3203 case TOK_BREAK:
3204 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3205 if (!pn)
3206 return NULL;
3207 if (!MatchLabel(cx, ts, pn))
3208 return NULL;
3209 stmt = tc->topStmt;
3210 label = pn->pn_atom;
3211 if (label) {
3212 for (; ; stmt = stmt->down) {
3213 if (!stmt) {
3214 js_ReportCompileErrorNumber(cx, ts,
3215 JSREPORT_TS | JSREPORT_ERROR,
3216 JSMSG_LABEL_NOT_FOUND);
3217 return NULL;
3218 }
3219 if (stmt->type == STMT_LABEL && stmt->atom == label)
3220 break;
3221 }
3222 } else {
3223 for (; ; stmt = stmt->down) {
3224 if (!stmt) {
3225 js_ReportCompileErrorNumber(cx, ts,
3226 JSREPORT_TS | JSREPORT_ERROR,
3227 JSMSG_TOUGH_BREAK);
3228 return NULL;
3229 }
3230 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
3231 break;
3232 }
3233 }
3234 if (label)
3235 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3236 break;
3237
3238 case TOK_CONTINUE:
3239 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3240 if (!pn)
3241 return NULL;
3242 if (!MatchLabel(cx, ts, pn))
3243 return NULL;
3244 stmt = tc->topStmt;
3245 label = pn->pn_atom;
3246 if (label) {
3247 for (stmt2 = NULL; ; stmt = stmt->down) {
3248 if (!stmt) {
3249 js_ReportCompileErrorNumber(cx, ts,
3250 JSREPORT_TS | JSREPORT_ERROR,
3251 JSMSG_LABEL_NOT_FOUND);
3252 return NULL;
3253 }
3254 if (stmt->type == STMT_LABEL) {
3255 if (stmt->atom == label) {
3256 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
3257 js_ReportCompileErrorNumber(cx, ts,
3258 JSREPORT_TS |
3259 JSREPORT_ERROR,
3260 JSMSG_BAD_CONTINUE);
3261 return NULL;
3262 }
3263 break;
3264 }
3265 } else {
3266 stmt2 = stmt;
3267 }
3268 }
3269 } else {
3270 for (; ; stmt = stmt->down) {
3271 if (!stmt) {
3272 js_ReportCompileErrorNumber(cx, ts,
3273 JSREPORT_TS | JSREPORT_ERROR,
3274 JSMSG_BAD_CONTINUE);
3275 return NULL;
3276 }
3277 if (STMT_IS_LOOP(stmt))
3278 break;
3279 }
3280 }
3281 if (label)
3282 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3283 break;
3284
3285 case TOK_WITH:
3286 pn = NewParseNode(cx, ts, PN_BINARY, tc);
3287 if (!pn)
3288 return NULL;
3289 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
3290 pn2 = Expr(cx, ts, tc);
3291 if (!pn2)
3292 return NULL;
3293 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
3294 pn->pn_left = pn2;
3295
3296 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
3297 pn2 = Statement(cx, ts, tc);
3298 if (!pn2)
3299 return NULL;
3300 js_PopStatement(tc);
3301
3302 pn->pn_pos.end = pn2->pn_pos.end;
3303 pn->pn_right = pn2;
3304 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3305 return pn;
3306
3307 case TOK_VAR:
3308 pn = Variables(cx, ts, tc);
3309 if (!pn)
3310 return NULL;
3311
3312 /* Tell js_EmitTree to generate a final POP. */
3313 pn->pn_extra |= PNX_POPVAR;
3314 break;
3315
3316 #if JS_HAS_BLOCK_SCOPE
3317 case TOK_LET:
3318 {
3319 JSStmtInfo **sip;
3320 JSObject *obj;
3321 JSAtom *atom;
3322
3323 /* Check for a let statement or let expression. */
3324 if (js_PeekToken(cx, ts) == TOK_LP) {
3325 pn = LetBlock(cx, ts, tc, JS_TRUE);
3326 if (!pn || pn->pn_op == JSOP_LEAVEBLOCK)
3327 return pn;
3328
3329 /* Let expressions require automatic semicolon insertion. */
3330 JS_ASSERT(pn->pn_type == TOK_SEMI ||
3331 pn->pn_op == JSOP_LEAVEBLOCKEXPR);
3332 break;
3333 }
3334
3335 /*
3336 * This is a let declaration. We must convert the nearest JSStmtInfo
3337 * that is a block or a switch body to be our scope statement. Further
3338 * let declarations in this block will find this scope statement and
3339 * use the same block object. If we are the first let declaration in
3340 * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a
3341 * scope statement) then we also need to set tc->blockNode to be our
3342 * TOK_LEXICALSCOPE.
3343 */
3344 sip = &tc->topScopeStmt;
3345 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
3346 if (STMT_MAYBE_SCOPE(stmt))
3347 break;
3348 if (stmt == *sip)
3349 sip = &stmt->downScope;
3350 }
3351
3352 if (stmt && (stmt->flags & SIF_SCOPE)) {
3353 JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom));
3354 obj = tc->blockChain;
3355 } else {
3356 if (!stmt) {
3357 /*
3358 * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749
3359 *
3360 * This is a hard case that requires more work. In particular,
3361 * in many cases, we're trying to emit code as we go. However,
3362 * this means that we haven't necessarily finished processing
3363 * all let declarations in the implicit top-level block when
3364 * we emit a reference to one of them. For now, punt on this
3365 * and pretend this is a var declaration.
3366 */
3367 CURRENT_TOKEN(ts).type = TOK_VAR;
3368 CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR;
3369
3370 pn = Variables(cx, ts, tc);
3371 if (!pn)
3372 return NULL;
3373 pn->pn_extra |= PNX_POPVAR;
3374 break;
3375 }
3376
3377 /* Convert the block statement into a scope statement. */
3378 obj = js_NewBlockObject(cx);
3379 if (!obj)
3380 return NULL;
3381 atom = js_AtomizeObject(cx, obj, 0);
3382 if (!atom)
3383 return NULL;
3384
3385 /*
3386 * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked
3387 * list stack, if it isn't already there. If it is there, but it
3388 * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
3389 * block.
3390 */
3391 JS_ASSERT(!(stmt->flags & SIF_SCOPE));
3392 stmt->flags |= SIF_SCOPE;
3393 if (stmt != *sip) {
3394 JS_ASSERT(!stmt->downScope);
3395 JS_ASSERT(stmt->type == STMT_BLOCK ||
3396 stmt->type == STMT_SWITCH ||
3397 stmt->type == STMT_TRY ||
3398 stmt->type == STMT_FINALLY);
3399 stmt->downScope = *sip;
3400 *sip = stmt;
3401 } else {
3402 JS_ASSERT(stmt->type == STMT_CATCH);
3403 JS_ASSERT(stmt->downScope);
3404 }
3405
3406 obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
3407 tc->blockChain = obj;
3408 stmt->atom = atom;
3409
3410 #ifdef DEBUG
3411 pn1 = tc->blockNode;
3412 JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE);
3413 #endif
3414
3415 /* Create a new lexical scope node for these statements. */
3416 pn1 = NewParseNode(cx, ts, PN_NAME, tc);
3417 if (!pn1)
3418 return NULL;
3419
3420 pn1->pn_type = TOK_LEXICALSCOPE;
3421 pn1->pn_op = JSOP_LEAVEBLOCK;
3422 pn1->pn_pos = tc->blockNode->pn_pos;
3423 pn1->pn_atom = atom;
3424 pn1->pn_expr = tc->blockNode;
3425 pn1->pn_slot = -1;
3426 pn1->pn_attrs = 0;
3427 tc->blockNode = pn1;
3428 }
3429
3430 pn = Variables(cx, ts, tc);
3431 if (!pn)
3432 return NULL;
3433 pn->pn_extra = PNX_POPVAR;
3434 break;
3435 }
3436 #endif /* JS_HAS_BLOCK_SCOPE */
3437
3438 case TOK_RETURN:
3439 pn = ReturnOrYield(cx, ts, tc, Expr);
3440 if (!pn)
3441 return NULL;
3442 break;
3443
3444 case TOK_LC:
3445 {
3446 uintN oldflags;
3447
3448 oldflags = tc->flags;
3449 tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT;
3450 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
3451 pn = Statements(cx, ts, tc);
3452 if (!pn)
3453 return NULL;
3454
3455 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
3456 js_PopStatement(tc);
3457
3458 /*
3459 * If we contain a function statement and our container is top-level
3460 * or another block, flag pn to preserve braces when decompiling.
3461 */
3462 if ((tc->flags & TCF_HAS_FUNCTION_STMT) &&
3463 (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) {
3464 pn->pn_extra |= PNX_NEEDBRACES;
3465 }
3466 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS));
3467 return pn;
3468 }
3469
3470 case TOK_EOL:
3471 case TOK_SEMI:
3472 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3473 if (!pn)
3474 return NULL;
3475 pn->pn_type = TOK_SEMI;
3476 pn->pn_kid = NULL;
3477 return pn;
3478
3479 #if JS_HAS_DEBUGGER_KEYWORD
3480 case TOK_DEBUGGER:
3481 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3482 if (!pn)
3483 return NULL;
3484 pn->pn_type = TOK_DEBUGGER;
3485 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3486 break;
3487 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3488
3489 #if JS_HAS_XML_SUPPORT
3490 case TOK_DEFAULT:
3491 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3492 if (!pn)
3493 return NULL;
3494 if (!js_MatchToken(cx, ts, TOK_NAME) ||
3495 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
3496 !js_MatchToken(cx, ts, TOK_NAME) ||
3497 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
3498 !js_MatchToken(cx, ts, TOK_ASSIGN) ||
3499 CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
3500 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3501 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
3502 return NULL;
3503 }
3504 pn2 = Expr(cx, ts, tc);
3505 if (!pn2)
3506 return NULL;
3507 pn->pn_op = JSOP_DEFXMLNS;
3508 pn->pn_pos.end = pn2->pn_pos.end;
3509 pn->pn_kid = pn2;
3510 tc->flags |= TCF_HAS_DEFXMLNS;
3511 break;
3512 #endif
3513
3514 case TOK_ERROR:
3515 return NULL;
3516
3517 default:
3518 #if JS_HAS_XML_SUPPORT
3519 expression:
3520 #endif
3521 js_UngetToken(ts);
3522 pn2 = Expr(cx, ts, tc);
3523 if (!pn2)
3524 return NULL;
3525
3526 if (js_PeekToken(cx, ts) == TOK_COLON) {
3527 if (pn2->pn_type != TOK_NAME) {
3528 js_ReportCompileErrorNumber(cx, ts,
3529 JSREPORT_TS | JSREPORT_ERROR,
3530 JSMSG_BAD_LABEL);
3531 return NULL;
3532 }
3533 label = pn2->pn_atom;
3534 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
3535 if (stmt->type == STMT_LABEL && stmt->atom == label) {
3536 js_ReportCompileErrorNumber(cx, ts,
3537 JSREPORT_TS | JSREPORT_ERROR,
3538 JSMSG_DUPLICATE_LABEL);
3539 return NULL;
3540 }
3541 }
3542 (void) js_GetToken(cx, ts);
3543
3544 /* Push a label struct and parse the statement. */
3545 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
3546 stmtInfo.atom = label;
3547 pn = Statement(cx, ts, tc);
3548 if (!pn)
3549 return NULL;
3550
3551 /* Normalize empty statement to empty block for the decompiler. */
3552 if (pn->pn_type == TOK_SEMI && !pn->pn_kid) {
3553 pn->pn_type = TOK_LC;
3554 pn->pn_arity = PN_LIST;
3555 PN_INIT_LIST(pn);
3556 }
3557
3558 /* Pop the label, set pn_expr, and return early. */
3559 js_PopStatement(tc);
3560 pn2->pn_type = TOK_COLON;
3561 pn2->pn_pos.end = pn->pn_pos.end;
3562 pn2->pn_expr = pn;
3563 return pn2;
3564 }
3565
3566 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3567 if (!pn)
3568 return NULL;
3569 pn->pn_type = TOK_SEMI;
3570 pn->pn_pos = pn2->pn_pos;
3571 pn->pn_kid = pn2;
3572 break;
3573 }
3574
3575 /* Check termination of this primitive statement. */
3576 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
3577 ts->flags |= TSF_OPERAND;
3578 tt = js_PeekTokenSameLine(cx, ts);
3579 ts->flags &= ~TSF_OPERAND;
3580 if (tt == TOK_ERROR)
3581 return NULL;
3582 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
3583 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3584 JSMSG_SEMI_BEFORE_STMNT);
3585 return NULL;
3586 }
3587 }
3588
3589 (void) js_MatchToken(cx, ts, TOK_SEMI);
3590 return pn;
3591 }
3592
3593 static JSParseNode *
Variables(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3594 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3595 {
3596 JSTokenType tt;
3597 JSBool let;
3598 JSStmtInfo *scopeStmt;
3599 BindData data;
3600 JSParseNode *pn, *pn2;
3601 JSStackFrame *fp;
3602 JSAtom *atom;
3603
3604 /*
3605 * The three options here are:
3606 * - TOK_LET: We are parsing a let declaration.
3607 * - TOK_LP: We are parsing the head of a let block.
3608 * - Otherwise, we're parsing var declarations.
3609 */
3610 tt = CURRENT_TOKEN(ts).type;
3611 let = (tt == TOK_LET || tt == TOK_LP);
3612 JS_ASSERT(let || tt == TOK_VAR);
3613
3614 /* Make sure that Statement set the tree context up correctly. */
3615 scopeStmt = tc->topScopeStmt;
3616 if (let) {
3617 while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) {
3618 JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt));
3619 scopeStmt = scopeStmt->downScope;
3620 }
3621 JS_ASSERT(scopeStmt);
3622 }
3623
3624 data.pn = NULL;
3625 data.ts = ts;
3626 data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op;
3627 data.binder = let ? BindLet : BindVarOrConst;
3628 pn = NewParseNode(cx, ts, PN_LIST, tc);
3629 if (!pn)
3630 return NULL;
3631 pn->pn_op = data.op;
3632 PN_INIT_LIST(pn);
3633
3634 /*
3635 * The tricky part of this code is to create special parsenode opcodes for
3636 * getting and setting variables (which will be stored as special slots in
3637 * the frame). The most complicated case is an eval() inside a function.
3638 * If the evaluated string references variables in the enclosing function,
3639 * then we need to generate the special variable opcodes. We determine
3640 * this by looking up the variable's id in the current variable object.
3641 * Fortunately, we can avoid doing this for let declared variables.
3642 */
3643 fp = cx->fp;
3644 if (let) {
3645 JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom));
3646 data.obj = tc->blockChain;
3647 data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj);
3648 data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS;
3649 } else {
3650 data.obj = fp->varobj;
3651 data.u.var.fun = fp->fun;
3652 data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj);
3653 if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) {
3654 /* We are compiling code inside a function */
3655 data.u.var.getter = js_GetLocalVariable;
3656 data.u.var.setter = js_SetLocalVariable;
3657 } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) {
3658 /* We are compiling code from an eval inside a function */
3659 data.u.var.getter = js_GetCallVariable;
3660 data.u.var.setter = js_SetCallVariable;
3661 } else {
3662 data.u.var.getter = data.u.var.clasp->getProperty;
3663 data.u.var.setter = data.u.var.clasp->setProperty;
3664 }
3665
3666 data.u.var.attrs = (data.op == JSOP_DEFCONST)
3667 ? JSPROP_PERMANENT | JSPROP_READONLY
3668 : JSPROP_PERMANENT;
3669 }
3670
3671 do {
3672 tt = js_GetToken(cx, ts);
3673 #if JS_HAS_DESTRUCTURING
3674 if (tt == TOK_LB || tt == TOK_LC) {
3675 pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
3676 if (!pn2)
3677 return NULL;
3678
3679 if ((tc->flags & TCF_IN_FOR_INIT) &&
3680 js_PeekToken(cx, ts) == TOK_IN) {
3681 if (!CheckDestructuring(cx, &data, pn2, NULL, tc))
3682 return NULL;
3683 PN_APPEND(pn, pn2);
3684 continue;
3685 }
3686
3687 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
3688 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
3689 goto bad_var_init;
3690
3691 pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP,
3692 pn2, AssignExpr(cx, ts, tc),
3693 tc);
3694 if (!pn2 ||
3695 !CheckDestructuring(cx, &data,
3696 pn2->pn_left, pn2->pn_right,
3697 tc)) {
3698 return NULL;
3699 }
3700 PN_APPEND(pn, pn2);
3701 continue;
3702 }
3703 #endif
3704
3705 if (tt != TOK_NAME) {
3706 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3707 JSMSG_NO_VARIABLE_NAME);
3708 return NULL;
3709 }
3710 atom = CURRENT_TOKEN(ts).t_atom;
3711 if (!data.binder(cx, &data, atom, tc))
3712 return NULL;
3713
3714 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
3715 if (!pn2)
3716 return NULL;
3717 pn2->pn_op = JSOP_NAME;
3718 pn2->pn_atom = atom;
3719 pn2->pn_expr = NULL;
3720 pn2->pn_slot = -1;
3721 pn2->pn_attrs = let ? 0 : data.u.var.attrs;
3722 PN_APPEND(pn, pn2);
3723
3724 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
3725 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP)
3726 goto bad_var_init;
3727
3728 pn2->pn_expr = AssignExpr(cx, ts, tc);
3729 if (!pn2->pn_expr)
3730 return NULL;
3731 pn2->pn_op = (!let && data.op == JSOP_DEFCONST)
3732 ? JSOP_SETCONST
3733 : JSOP_SETNAME;
3734 if (!let && atom == cx->runtime->atomState.argumentsAtom)
3735 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3736 }
3737 } while (js_MatchToken(cx, ts, TOK_COMMA));
3738
3739 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
3740 return pn;
3741
3742 bad_var_init:
3743 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3744 JSMSG_BAD_VAR_INIT);
3745 return NULL;
3746 }
3747
3748 static JSParseNode *
Expr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3749 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3750 {
3751 JSParseNode *pn, *pn2;
3752
3753 pn = AssignExpr(cx, ts, tc);
3754 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
3755 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
3756 if (!pn2)
3757 return NULL;
3758 pn2->pn_pos.begin = pn->pn_pos.begin;
3759 PN_INIT_LIST_1(pn2, pn);
3760 pn = pn2;
3761 do {
3762 #if JS_HAS_GENERATORS
3763 pn2 = PN_LAST(pn);
3764 if (pn2->pn_type == TOK_YIELD) {
3765 js_ReportCompileErrorNumber(cx, pn2,
3766 JSREPORT_PN | JSREPORT_ERROR,
3767 JSMSG_BAD_YIELD_SYNTAX);
3768 return NULL;
3769 }
3770 #endif
3771 pn2 = AssignExpr(cx, ts, tc);
3772 if (!pn2)
3773 return NULL;
3774 PN_APPEND(pn, pn2);
3775 } while (js_MatchToken(cx, ts, TOK_COMMA));
3776 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
3777 }
3778 return pn;
3779 }
3780
3781 static JSParseNode *
AssignExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3782 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3783 {
3784 JSParseNode *pn, *pn2;
3785 JSTokenType tt;
3786 JSOp op;
3787
3788 CHECK_RECURSION();
3789
3790 #if JS_HAS_GENERATORS
3791 ts->flags |= TSF_OPERAND;
3792 if (js_MatchToken(cx, ts, TOK_YIELD)) {
3793 ts->flags &= ~TSF_OPERAND;
3794 return ReturnOrYield(cx, ts, tc, AssignExpr);
3795 }
3796 ts->flags &= ~TSF_OPERAND;
3797 #endif
3798
3799 pn = CondExpr(cx, ts, tc);
3800 if (!pn)
3801 return NULL;
3802
3803 tt = js_GetToken(cx, ts);
3804 #if JS_HAS_GETTER_SETTER
3805 if (tt == TOK_NAME) {
3806 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
3807 if (tt == TOK_ERROR)
3808 return NULL;
3809 }
3810 #endif
3811 if (tt != TOK_ASSIGN) {
3812 js_UngetToken(ts);
3813 return pn;
3814 }
3815
3816 op = CURRENT_TOKEN(ts).t_op;
3817 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
3818 continue;
3819 switch (pn2->pn_type) {
3820 case TOK_NAME:
3821 pn2->pn_op = JSOP_SETNAME;
3822 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
3823 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3824 break;
3825 case TOK_DOT:
3826 pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD)
3827 ? JSOP_SETMETHOD
3828 : JSOP_SETPROP;
3829 break;
3830 case TOK_LB:
3831 pn2->pn_op = JSOP_SETELEM;
3832 break;
3833 #if JS_HAS_DESTRUCTURING
3834 case TOK_RB:
3835 case TOK_RC:
3836 if (op != JSOP_NOP) {
3837 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3838 JSMSG_BAD_DESTRUCT_ASS);
3839 return NULL;
3840 }
3841 pn = AssignExpr(cx, ts, tc);
3842 if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc))
3843 return NULL;
3844 return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc);
3845 #endif
3846 #if JS_HAS_LVALUE_RETURN
3847 case TOK_LP:
3848 JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
3849 pn2->pn_op = JSOP_SETCALL;
3850 break;
3851 #endif
3852 #if JS_HAS_XML_SUPPORT
3853 case TOK_UNARYOP:
3854 if (pn2->pn_op == JSOP_XMLNAME) {
3855 pn2->pn_op = JSOP_SETXMLNAME;
3856 break;
3857 }
3858 /* FALL THROUGH */
3859 #endif
3860 default:
3861 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3862 JSMSG_BAD_LEFTSIDE_OF_ASS);
3863 return NULL;
3864 }
3865
3866 return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
3867 }
3868
3869 static JSParseNode *
CondExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3870 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3871 {
3872 JSParseNode *pn, *pn1, *pn2, *pn3;
3873 uintN oldflags;
3874
3875 pn = OrExpr(cx, ts, tc);
3876 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
3877 pn1 = pn;
3878 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
3879 if (!pn)
3880 return NULL;
3881 /*
3882 * Always accept the 'in' operator in the middle clause of a ternary,
3883 * where it's unambiguous, even if we might be parsing the init of a
3884 * for statement.
3885 */
3886 oldflags = tc->flags;
3887 tc->flags &= ~TCF_IN_FOR_INIT;
3888 pn2 = AssignExpr(cx, ts, tc);
3889 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3890
3891 if (!pn2)
3892 return NULL;
3893 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
3894 pn3 = AssignExpr(cx, ts, tc);
3895 if (!pn3)
3896 return NULL;
3897 pn->pn_pos.begin = pn1->pn_pos.begin;
3898 pn->pn_pos.end = pn3->pn_pos.end;
3899 pn->pn_kid1 = pn1;
3900 pn->pn_kid2 = pn2;
3901 pn->pn_kid3 = pn3;
3902 }
3903 return pn;
3904 }
3905
3906 static JSParseNode *
OrExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3907 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3908 {
3909 JSParseNode *pn;
3910
3911 pn = AndExpr(cx, ts, tc);
3912 if (pn && js_MatchToken(cx, ts, TOK_OR))
3913 pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
3914 return pn;
3915 }
3916
3917 static JSParseNode *
AndExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3918 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3919 {
3920 JSParseNode *pn;
3921
3922 pn = BitOrExpr(cx, ts, tc);
3923 if (pn && js_MatchToken(cx, ts, TOK_AND))
3924 pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
3925 return pn;
3926 }
3927
3928 static JSParseNode *
BitOrExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3929 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3930 {
3931 JSParseNode *pn;
3932
3933 pn = BitXorExpr(cx, ts, tc);
3934 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
3935 pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
3936 tc);
3937 }
3938 return pn;
3939 }
3940
3941 static JSParseNode *
BitXorExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3942 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3943 {
3944 JSParseNode *pn;
3945
3946 pn = BitAndExpr(cx, ts, tc);
3947 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
3948 pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
3949 tc);
3950 }
3951 return pn;
3952 }
3953
3954 static JSParseNode *
BitAndExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3955 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3956 {
3957 JSParseNode *pn;
3958
3959 pn = EqExpr(cx, ts, tc);
3960 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
3961 pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
3962 return pn;
3963 }
3964
3965 static JSParseNode *
EqExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3966 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3967 {
3968 JSParseNode *pn;
3969 JSOp op;
3970
3971 pn = RelExpr(cx, ts, tc);
3972 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
3973 op = CURRENT_TOKEN(ts).t_op;
3974 pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
3975 }
3976 return pn;
3977 }
3978
3979 static JSParseNode *
RelExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3980 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3981 {
3982 JSParseNode *pn;
3983 JSTokenType tt;
3984 JSOp op;
3985 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
3986
3987 /*
3988 * Uses of the in operator in ShiftExprs are always unambiguous,
3989 * so unset the flag that prohibits recognizing it.
3990 */
3991 tc->flags &= ~TCF_IN_FOR_INIT;
3992
3993 pn = ShiftExpr(cx, ts, tc);
3994 while (pn &&
3995 (js_MatchToken(cx, ts, TOK_RELOP) ||
3996 /*
3997 * Recognize the 'in' token as an operator only if we're not
3998 * currently in the init expr of a for loop.
3999 */
4000 (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) ||
4001 js_MatchToken(cx, ts, TOK_INSTANCEOF))) {
4002 tt = CURRENT_TOKEN(ts).type;
4003 op = CURRENT_TOKEN(ts).t_op;
4004 pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
4005 }
4006 /* Restore previous state of inForInit flag. */
4007 tc->flags |= inForInitFlag;
4008
4009 return pn;
4010 }
4011
4012 static JSParseNode *
ShiftExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4013 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4014 {
4015 JSParseNode *pn;
4016 JSOp op;
4017
4018 pn = AddExpr(cx, ts, tc);
4019 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
4020 op = CURRENT_TOKEN(ts).t_op;
4021 pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
4022 }
4023 return pn;
4024 }
4025
4026 static JSParseNode *
AddExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4027 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4028 {
4029 JSParseNode *pn;
4030 JSTokenType tt;
4031 JSOp op;
4032
4033 pn = MulExpr(cx, ts, tc);
4034 while (pn &&
4035 (js_MatchToken(cx, ts, TOK_PLUS) ||
4036 js_MatchToken(cx, ts, TOK_MINUS))) {
4037 tt = CURRENT_TOKEN(ts).type;
4038 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
4039 pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
4040 }
4041 return pn;
4042 }
4043
4044 static JSParseNode *
MulExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4045 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4046 {
4047 JSParseNode *pn;
4048 JSTokenType tt;
4049 JSOp op;
4050
4051 pn = UnaryExpr(cx, ts, tc);
4052 while (pn &&
4053 (js_MatchToken(cx, ts, TOK_STAR) ||
4054 js_MatchToken(cx, ts, TOK_DIVOP))) {
4055 tt = CURRENT_TOKEN(ts).type;
4056 op = CURRENT_TOKEN(ts).t_op;
4057 pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
4058 }
4059 return pn;
4060 }
4061
4062 static JSParseNode *
SetLvalKid(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSParseNode * kid,const char * name)4063 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
4064 const char *name)
4065 {
4066 while (kid->pn_type == TOK_RP)
4067 kid = kid->pn_kid;
4068 if (kid->pn_type != TOK_NAME &&
4069 kid->pn_type != TOK_DOT &&
4070 #if JS_HAS_LVALUE_RETURN
4071 (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
4072 #endif
4073 #if JS_HAS_XML_SUPPORT
4074 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
4075 #endif
4076 kid->pn_type != TOK_LB) {
4077 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4078 JSMSG_BAD_OPERAND, name);
4079 return NULL;
4080 }
4081 pn->pn_kid = kid;
4082 return kid;
4083 }
4084
4085 static const char incop_name_str[][10] = {"increment", "decrement"};
4086
4087 static JSBool
SetIncOpKid(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * pn,JSParseNode * kid,JSTokenType tt,JSBool preorder)4088 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4089 JSParseNode *pn, JSParseNode *kid,
4090 JSTokenType tt, JSBool preorder)
4091 {
4092 JSOp op;
4093
4094 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
4095 if (!kid)
4096 return JS_FALSE;
4097 switch (kid->pn_type) {
4098 case TOK_NAME:
4099 op = (tt == TOK_INC)
4100 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
4101 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
4102 if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
4103 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4104 break;
4105
4106 case TOK_DOT:
4107 op = (tt == TOK_INC)
4108 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
4109 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
4110 break;
4111
4112 #if JS_HAS_LVALUE_RETURN
4113 case TOK_LP:
4114 JS_ASSERT(kid->pn_op == JSOP_CALL);
4115 kid->pn_op = JSOP_SETCALL;
4116 /* FALL THROUGH */
4117 #endif
4118 #if JS_HAS_XML_SUPPORT
4119 case TOK_UNARYOP:
4120 if (kid->pn_op == JSOP_XMLNAME)
4121 kid->pn_op = JSOP_SETXMLNAME;
4122 /* FALL THROUGH */
4123 #endif
4124 case TOK_LB:
4125 op = (tt == TOK_INC)
4126 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
4127 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
4128 break;
4129
4130 default:
4131 JS_ASSERT(0);
4132 op = JSOP_NOP;
4133 }
4134 pn->pn_op = op;
4135 return JS_TRUE;
4136 }
4137
4138 static JSParseNode *
UnaryExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4139 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4140 {
4141 JSTokenType tt;
4142 JSParseNode *pn, *pn2;
4143
4144 CHECK_RECURSION();
4145
4146 ts->flags |= TSF_OPERAND;
4147 tt = js_GetToken(cx, ts);
4148 ts->flags &= ~TSF_OPERAND;
4149
4150 switch (tt) {
4151 case TOK_UNARYOP:
4152 case TOK_PLUS:
4153 case TOK_MINUS:
4154 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4155 if (!pn)
4156 return NULL;
4157 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
4158 pn->pn_op = CURRENT_TOKEN(ts).t_op;
4159 pn2 = UnaryExpr(cx, ts, tc);
4160 if (!pn2)
4161 return NULL;
4162 pn->pn_pos.end = pn2->pn_pos.end;
4163 pn->pn_kid = pn2;
4164 break;
4165
4166 case TOK_INC:
4167 case TOK_DEC:
4168 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4169 if (!pn)
4170 return NULL;
4171 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
4172 if (!pn2)
4173 return NULL;
4174 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
4175 return NULL;
4176 pn->pn_pos.end = pn2->pn_pos.end;
4177 break;
4178
4179 case TOK_DELETE:
4180 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4181 if (!pn)
4182 return NULL;
4183 pn2 = UnaryExpr(cx, ts, tc);
4184 if (!pn2)
4185 return NULL;
4186 pn->pn_pos.end = pn2->pn_pos.end;
4187
4188 /*
4189 * Under ECMA3, deleting any unary expression is valid -- it simply
4190 * returns true. Here we strip off any parentheses.
4191 */
4192 while (pn2->pn_type == TOK_RP)
4193 pn2 = pn2->pn_kid;
4194 pn->pn_kid = pn2;
4195 break;
4196
4197 case TOK_ERROR:
4198 return NULL;
4199
4200 default:
4201 js_UngetToken(ts);
4202 pn = MemberExpr(cx, ts, tc, JS_TRUE);
4203 if (!pn)
4204 return NULL;
4205
4206 /* Don't look across a newline boundary for a postfix incop. */
4207 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
4208 ts->flags |= TSF_OPERAND;
4209 tt = js_PeekTokenSameLine(cx, ts);
4210 ts->flags &= ~TSF_OPERAND;
4211 if (tt == TOK_INC || tt == TOK_DEC) {
4212 (void) js_GetToken(cx, ts);
4213 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
4214 if (!pn2)
4215 return NULL;
4216 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
4217 return NULL;
4218 pn2->pn_pos.begin = pn->pn_pos.begin;
4219 pn = pn2;
4220 }
4221 }
4222 break;
4223 }
4224 return pn;
4225 }
4226
4227 static JSBool
ArgumentList(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * listNode)4228 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4229 JSParseNode *listNode)
4230 {
4231 JSBool matched;
4232
4233 ts->flags |= TSF_OPERAND;
4234 matched = js_MatchToken(cx, ts, TOK_RP);
4235 ts->flags &= ~TSF_OPERAND;
4236 if (!matched) {
4237 do {
4238 JSParseNode *argNode = AssignExpr(cx, ts, tc);
4239 if (!argNode)
4240 return JS_FALSE;
4241 #if JS_HAS_GENERATORS
4242 if (argNode->pn_type == TOK_YIELD) {
4243 js_ReportCompileErrorNumber(cx, argNode,
4244 JSREPORT_PN | JSREPORT_ERROR,
4245 JSMSG_BAD_YIELD_SYNTAX);
4246 return JS_FALSE;
4247 }
4248 #endif
4249 PN_APPEND(listNode, argNode);
4250 } while (js_MatchToken(cx, ts, TOK_COMMA));
4251
4252 if (js_GetToken(cx, ts) != TOK_RP) {
4253 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4254 JSMSG_PAREN_AFTER_ARGS);
4255 return JS_FALSE;
4256 }
4257 }
4258 return JS_TRUE;
4259 }
4260
4261 static JSParseNode *
MemberExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowCallSyntax)4262 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4263 JSBool allowCallSyntax)
4264 {
4265 JSParseNode *pn, *pn2, *pn3;
4266 JSTokenType tt;
4267
4268 CHECK_RECURSION();
4269
4270 /* Check for new expression first. */
4271 ts->flags |= TSF_OPERAND;
4272 tt = js_GetToken(cx, ts);
4273 ts->flags &= ~TSF_OPERAND;
4274 if (tt == TOK_NEW) {
4275 pn = NewParseNode(cx, ts, PN_LIST, tc);
4276 if (!pn)
4277 return NULL;
4278 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
4279 if (!pn2)
4280 return NULL;
4281 pn->pn_op = JSOP_NEW;
4282 PN_INIT_LIST_1(pn, pn2);
4283 pn->pn_pos.begin = pn2->pn_pos.begin;
4284
4285 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
4286 return NULL;
4287 if (pn->pn_count > ARGC_LIMIT) {
4288 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4289 JSMSG_TOO_MANY_CON_ARGS);
4290 return NULL;
4291 }
4292 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
4293 } else {
4294 pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
4295 if (!pn)
4296 return NULL;
4297
4298 if (pn->pn_type == TOK_ANYNAME ||
4299 pn->pn_type == TOK_AT ||
4300 pn->pn_type == TOK_DBLCOLON) {
4301 pn2 = NewOrRecycledNode(cx, tc);
4302 if (!pn2)
4303 return NULL;
4304 pn2->pn_type = TOK_UNARYOP;
4305 pn2->pn_pos = pn->pn_pos;
4306 pn2->pn_op = JSOP_XMLNAME;
4307 pn2->pn_arity = PN_UNARY;
4308 pn2->pn_kid = pn;
4309 pn2->pn_next = NULL;
4310 pn2->pn_ts = ts;
4311 pn2->pn_source = NULL;
4312 pn = pn2;
4313 }
4314 }
4315
4316 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
4317 if (tt == TOK_DOT) {
4318 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
4319 if (!pn2)
4320 return NULL;
4321 pn2->pn_slot = -1;
4322 pn2->pn_attrs = 0;
4323 #if JS_HAS_XML_SUPPORT
4324 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
4325 tt = js_GetToken(cx, ts);
4326 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
4327 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
4328 if (!pn3)
4329 return NULL;
4330 tt = pn3->pn_type;
4331 if (tt == TOK_NAME ||
4332 (tt == TOK_DBLCOLON &&
4333 pn3->pn_arity == PN_NAME &&
4334 pn3->pn_expr->pn_type == TOK_FUNCTION)) {
4335 pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD;
4336 pn2->pn_expr = pn;
4337 pn2->pn_atom = pn3->pn_atom;
4338 RecycleTree(pn3, tc);
4339 } else {
4340 if (TOKEN_TYPE_IS_XML(tt)) {
4341 pn2->pn_type = TOK_LB;
4342 pn2->pn_op = JSOP_GETELEM;
4343 } else if (tt == TOK_RP) {
4344 JSParseNode *group = pn3;
4345
4346 /* Recycle the useless TOK_RP/JSOP_GROUP node. */
4347 pn3 = group->pn_kid;
4348 group->pn_kid = NULL;
4349 RecycleTree(group, tc);
4350 pn2->pn_type = TOK_FILTER;
4351 pn2->pn_op = JSOP_FILTER;
4352
4353 /* A filtering predicate is like a with statement. */
4354 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4355 } else {
4356 js_ReportCompileErrorNumber(cx, ts,
4357 JSREPORT_TS | JSREPORT_ERROR,
4358 JSMSG_NAME_AFTER_DOT);
4359 return NULL;
4360 }
4361 pn2->pn_arity = PN_BINARY;
4362 pn2->pn_left = pn;
4363 pn2->pn_right = pn3;
4364 }
4365 #else
4366 ts->flags |= TSF_KEYWORD_IS_NAME;
4367 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
4368 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4369 pn2->pn_op = JSOP_GETPROP;
4370 pn2->pn_expr = pn;
4371 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
4372 #endif
4373 pn2->pn_pos.begin = pn->pn_pos.begin;
4374 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4375 #if JS_HAS_XML_SUPPORT
4376 } else if (tt == TOK_DBLDOT) {
4377 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
4378 if (!pn2)
4379 return NULL;
4380 ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
4381 tt = js_GetToken(cx, ts);
4382 ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
4383 pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
4384 if (!pn3)
4385 return NULL;
4386 tt = pn3->pn_type;
4387 if (tt == TOK_NAME) {
4388 pn3->pn_type = TOK_STRING;
4389 pn3->pn_arity = PN_NULLARY;
4390 pn3->pn_op = JSOP_QNAMEPART;
4391 } else if (!TOKEN_TYPE_IS_XML(tt)) {
4392 js_ReportCompileErrorNumber(cx, ts,
4393 JSREPORT_TS | JSREPORT_ERROR,
4394 JSMSG_NAME_AFTER_DOT);
4395 return NULL;
4396 }
4397 pn2->pn_op = JSOP_DESCENDANTS;
4398 pn2->pn_left = pn;
4399 pn2->pn_right = pn3;
4400 pn2->pn_pos.begin = pn->pn_pos.begin;
4401 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4402 #endif
4403 } else if (tt == TOK_LB) {
4404 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
4405 if (!pn2)
4406 return NULL;
4407 pn3 = Expr(cx, ts, tc);
4408 if (!pn3)
4409 return NULL;
4410
4411 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
4412 pn2->pn_pos.begin = pn->pn_pos.begin;
4413 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4414
4415 /* Optimize o['p'] to o.p by rewriting pn2. */
4416 if (pn3->pn_type == TOK_STRING) {
4417 pn2->pn_type = TOK_DOT;
4418 pn2->pn_op = JSOP_GETPROP;
4419 pn2->pn_arity = PN_NAME;
4420 pn2->pn_expr = pn;
4421 pn2->pn_atom = pn3->pn_atom;
4422 } else {
4423 pn2->pn_op = JSOP_GETELEM;
4424 pn2->pn_left = pn;
4425 pn2->pn_right = pn3;
4426 }
4427 } else if (allowCallSyntax && tt == TOK_LP) {
4428 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
4429 if (!pn2)
4430 return NULL;
4431
4432 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
4433 pn2->pn_op = JSOP_CALL;
4434 if (pn->pn_op == JSOP_NAME &&
4435 pn->pn_atom == cx->runtime->atomState.evalAtom) {
4436 pn2->pn_op = JSOP_EVAL;
4437 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4438 }
4439
4440 PN_INIT_LIST_1(pn2, pn);
4441 pn2->pn_pos.begin = pn->pn_pos.begin;
4442
4443 if (!ArgumentList(cx, ts, tc, pn2))
4444 return NULL;
4445 if (pn2->pn_count > ARGC_LIMIT) {
4446 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4447 JSMSG_TOO_MANY_FUN_ARGS);
4448 return NULL;
4449 }
4450 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4451 } else {
4452 js_UngetToken(ts);
4453 return pn;
4454 }
4455
4456 pn = pn2;
4457 }
4458 if (tt == TOK_ERROR)
4459 return NULL;
4460 return pn;
4461 }
4462
4463 static JSParseNode *
BracketedExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4464 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4465 {
4466 uintN oldflags;
4467 JSParseNode *pn;
4468
4469 /*
4470 * Always accept the 'in' operator in a parenthesized expression,
4471 * where it's unambiguous, even if we might be parsing the init of a
4472 * for statement.
4473 */
4474 oldflags = tc->flags;
4475 tc->flags &= ~TCF_IN_FOR_INIT;
4476 pn = Expr(cx, ts, tc);
4477 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
4478 return pn;
4479 }
4480
4481 #if JS_HAS_XML_SUPPORT
4482
4483 static JSParseNode *
EndBracketedExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4484 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4485 {
4486 JSParseNode *pn;
4487
4488 pn = BracketedExpr(cx, ts, tc);
4489 if (!pn)
4490 return NULL;
4491
4492 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
4493 return pn;
4494 }
4495
4496 /*
4497 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
4498 *
4499 * AttributeIdentifier:
4500 * @ PropertySelector
4501 * @ QualifiedIdentifier
4502 * @ [ Expression ]
4503 *
4504 * PropertySelector:
4505 * Identifier
4506 * *
4507 *
4508 * QualifiedIdentifier:
4509 * PropertySelector :: PropertySelector
4510 * PropertySelector :: [ Expression ]
4511 *
4512 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
4513 *
4514 * AttributeIdentifier:
4515 * @ QualifiedIdentifier
4516 * @ [ Expression ]
4517 *
4518 * PropertySelector:
4519 * Identifier
4520 * *
4521 *
4522 * QualifiedIdentifier:
4523 * PropertySelector :: PropertySelector
4524 * PropertySelector :: [ Expression ]
4525 * PropertySelector
4526 *
4527 * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics
4528 * for that rule to result in a name node, but ECMA-357 extends the grammar
4529 * to include PrimaryExpression: QualifiedIdentifier, we must factor further:
4530 *
4531 * QualifiedIdentifier:
4532 * PropertySelector QualifiedSuffix
4533 *
4534 * QualifiedSuffix:
4535 * :: PropertySelector
4536 * :: [ Expression ]
4537 * /nothing/
4538 *
4539 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
4540 *
4541 * PrimaryExpression:
4542 * Identifier QualifiedSuffix
4543 *
4544 * We hoist the :: match into callers of QualifiedSuffix, in order to tweak
4545 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
4546 */
4547 static JSParseNode *
PropertySelector(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4548 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4549 {
4550 JSParseNode *pn;
4551
4552 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4553 if (!pn)
4554 return NULL;
4555 if (pn->pn_type == TOK_STAR) {
4556 pn->pn_type = TOK_ANYNAME;
4557 pn->pn_op = JSOP_ANYNAME;
4558 pn->pn_atom = cx->runtime->atomState.starAtom;
4559 } else {
4560 JS_ASSERT(pn->pn_type == TOK_NAME);
4561 pn->pn_op = JSOP_QNAMEPART;
4562 pn->pn_arity = PN_NAME;
4563 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
4564 pn->pn_expr = NULL;
4565 pn->pn_slot = -1;
4566 pn->pn_attrs = 0;
4567 }
4568 return pn;
4569 }
4570
4571 static JSParseNode *
QualifiedSuffix(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSTreeContext * tc)4572 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
4573 JSTreeContext *tc)
4574 {
4575 JSParseNode *pn2, *pn3;
4576 JSTokenType tt;
4577
4578 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
4579 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
4580 if (!pn2)
4581 return NULL;
4582
4583 /* Left operand of :: must be evaluated if it is an identifier. */
4584 if (pn->pn_op == JSOP_QNAMEPART)
4585 pn->pn_op = JSOP_NAME;
4586
4587 ts->flags |= TSF_KEYWORD_IS_NAME;
4588 tt = js_GetToken(cx, ts);
4589 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4590 if (tt == TOK_STAR || tt == TOK_NAME) {
4591 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
4592 pn2->pn_op = JSOP_QNAMECONST;
4593 pn2->pn_atom = (tt == TOK_STAR)
4594 ? cx->runtime->atomState.starAtom
4595 : CURRENT_TOKEN(ts).t_atom;
4596 pn2->pn_expr = pn;
4597 pn2->pn_slot = -1;
4598 pn2->pn_attrs = 0;
4599 return pn2;
4600 }
4601
4602 if (tt != TOK_LB) {
4603 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4604 JSMSG_SYNTAX_ERROR);
4605 return NULL;
4606 }
4607 pn3 = EndBracketedExpr(cx, ts, tc);
4608 if (!pn3)
4609 return NULL;
4610
4611 pn2->pn_op = JSOP_QNAME;
4612 pn2->pn_arity = PN_BINARY;
4613 pn2->pn_left = pn;
4614 pn2->pn_right = pn3;
4615 return pn2;
4616 }
4617
4618 static JSParseNode *
QualifiedIdentifier(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4619 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4620 {
4621 JSParseNode *pn;
4622
4623 pn = PropertySelector(cx, ts, tc);
4624 if (!pn)
4625 return NULL;
4626 if (js_MatchToken(cx, ts, TOK_DBLCOLON))
4627 pn = QualifiedSuffix(cx, ts, pn, tc);
4628 return pn;
4629 }
4630
4631 static JSParseNode *
AttributeIdentifier(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4632 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4633 {
4634 JSParseNode *pn, *pn2;
4635 JSTokenType tt;
4636
4637 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
4638 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4639 if (!pn)
4640 return NULL;
4641 pn->pn_op = JSOP_TOATTRNAME;
4642 ts->flags |= TSF_KEYWORD_IS_NAME;
4643 tt = js_GetToken(cx, ts);
4644 ts->flags &= ~TSF_KEYWORD_IS_NAME;
4645 if (tt == TOK_STAR || tt == TOK_NAME) {
4646 pn2 = QualifiedIdentifier(cx, ts, tc);
4647 } else if (tt == TOK_LB) {
4648 pn2 = EndBracketedExpr(cx, ts, tc);
4649 } else {
4650 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4651 JSMSG_SYNTAX_ERROR);
4652 return NULL;
4653 }
4654 if (!pn2)
4655 return NULL;
4656 pn->pn_kid = pn2;
4657 return pn;
4658 }
4659
4660 /*
4661 * Make a TOK_LC unary node whose pn_kid is an expression.
4662 */
4663 static JSParseNode *
XMLExpr(JSContext * cx,JSTokenStream * ts,JSBool inTag,JSTreeContext * tc)4664 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
4665 {
4666 JSParseNode *pn, *pn2;
4667 uintN oldflags;
4668
4669 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
4670 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4671 if (!pn)
4672 return NULL;
4673
4674 /*
4675 * Turn off XML tag mode, but don't restore it after parsing this braced
4676 * expression. Instead, simply restore ts's old flags. This is required
4677 * because XMLExpr is called both from within a tag, and from within text
4678 * contained in an element, but outside of any start, end, or point tag.
4679 */
4680 oldflags = ts->flags;
4681 ts->flags = oldflags & ~TSF_XMLTAGMODE;
4682 pn2 = Expr(cx, ts, tc);
4683 if (!pn2)
4684 return NULL;
4685
4686 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
4687 ts->flags = oldflags;
4688 pn->pn_kid = pn2;
4689 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
4690 return pn;
4691 }
4692
4693 /*
4694 * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
4695 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
4696 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
4697 * child of a container tag.
4698 */
4699 static JSParseNode *
XMLAtomNode(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4700 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4701 {
4702 JSParseNode *pn;
4703 JSToken *tp;
4704
4705 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4706 if (!pn)
4707 return NULL;
4708 tp = &CURRENT_TOKEN(ts);
4709 pn->pn_op = tp->t_op;
4710 pn->pn_atom = tp->t_atom;
4711 if (tp->type == TOK_XMLPI)
4712 pn->pn_atom2 = tp->t_atom2;
4713 return pn;
4714 }
4715
4716 /*
4717 * Parse the productions:
4718 *
4719 * XMLNameExpr:
4720 * XMLName XMLNameExpr?
4721 * { Expr } XMLNameExpr?
4722 *
4723 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
4724 * a list of names and/or expressions, a single expression, or a single name.
4725 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
4726 * will be TOK_LC.
4727 */
4728 static JSParseNode *
XMLNameExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)4729 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
4730 {
4731 JSParseNode *pn, *pn2, *list;
4732 JSTokenType tt;
4733
4734 pn = list = NULL;
4735 do {
4736 tt = CURRENT_TOKEN(ts).type;
4737 if (tt == TOK_LC) {
4738 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
4739 if (!pn2)
4740 return NULL;
4741 } else {
4742 JS_ASSERT(tt == TOK_XMLNAME);
4743 pn2 = XMLAtomNode(cx, ts, tc);
4744 if (!pn2)
4745 return NULL;
4746 }
4747
4748 if (!pn) {
4749 pn = pn2;
4750 } else {
4751 if (!list) {
4752 list = NewParseNode(cx, ts, PN_LIST, tc);
4753 if (!list)
4754 return NULL;
4755 list->pn_type = TOK_XMLNAME;
4756 list->pn_pos.begin = pn->pn_pos.begin;
4757 PN_INIT_LIST_1(list, pn);
4758 list->pn_extra = PNX_CANTFOLD;
4759 pn = list;
4760 }
4761 pn->pn_pos.end = pn2->pn_pos.end;
4762 PN_APPEND(pn, pn2);
4763 }
4764 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
4765
4766 js_UngetToken(ts);
4767 return pn;
4768 }
4769
4770 /*
4771 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
4772 * at compile time into a JSXML tree.
4773 */
4774 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
4775 ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \
4776 : (pn)->pn_type != TOK_LC)
4777
4778 /*
4779 * Parse the productions:
4780 *
4781 * XMLTagContent:
4782 * XMLNameExpr
4783 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
4784 * XMLTagContent S XMLNameExpr S? = S? { Expr }
4785 *
4786 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
4787 * produces a list of name and attribute values and/or braced expressions, a
4788 * single expression, or a single name.
4789 *
4790 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
4791 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
4792 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
4793 * we parsed exactly one expression.
4794 */
4795 static JSParseNode *
XMLTagContent(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSTokenType tagtype,JSAtom ** namep)4796 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4797 JSTokenType tagtype, JSAtom **namep)
4798 {
4799 JSParseNode *pn, *pn2, *list;
4800 JSTokenType tt;
4801
4802 pn = XMLNameExpr(cx, ts, tc);
4803 if (!pn)
4804 return NULL;
4805 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
4806 list = NULL;
4807
4808 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
4809 tt = js_GetToken(cx, ts);
4810 if (tt != TOK_XMLNAME && tt != TOK_LC) {
4811 js_UngetToken(ts);
4812 break;
4813 }
4814
4815 pn2 = XMLNameExpr(cx, ts, tc);
4816 if (!pn2)
4817 return NULL;
4818 if (!list) {
4819 list = NewParseNode(cx, ts, PN_LIST, tc);
4820 if (!list)
4821 return NULL;
4822 list->pn_type = tagtype;
4823 list->pn_pos.begin = pn->pn_pos.begin;
4824 PN_INIT_LIST_1(list, pn);
4825 pn = list;
4826 }
4827 PN_APPEND(pn, pn2);
4828 if (!XML_FOLDABLE(pn2))
4829 pn->pn_extra |= PNX_CANTFOLD;
4830
4831 js_MatchToken(cx, ts, TOK_XMLSPACE);
4832 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
4833 js_MatchToken(cx, ts, TOK_XMLSPACE);
4834
4835 tt = js_GetToken(cx, ts);
4836 if (tt == TOK_XMLATTR) {
4837 pn2 = XMLAtomNode(cx, ts, tc);
4838 } else if (tt == TOK_LC) {
4839 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
4840 pn->pn_extra |= PNX_CANTFOLD;
4841 } else {
4842 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4843 JSMSG_BAD_XML_ATTR_VALUE);
4844 return NULL;
4845 }
4846 if (!pn2)
4847 return NULL;
4848 pn->pn_pos.end = pn2->pn_pos.end;
4849 PN_APPEND(pn, pn2);
4850 }
4851
4852 return pn;
4853 }
4854
4855 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
4856 JS_BEGIN_MACRO \
4857 if ((tt) <= TOK_EOF) { \
4858 if ((tt) == TOK_EOF) { \
4859 js_ReportCompileErrorNumber(cx, ts, \
4860 JSREPORT_TS | JSREPORT_ERROR, \
4861 JSMSG_END_OF_XML_SOURCE); \
4862 } \
4863 return result; \
4864 } \
4865 JS_END_MACRO
4866
4867 static JSParseNode *
4868 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4869 JSBool allowList);
4870
4871 /*
4872 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
4873 * that opens the end tag for the container.
4874 */
4875 static JSBool
XMLElementContent(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSTreeContext * tc)4876 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
4877 JSTreeContext *tc)
4878 {
4879 JSTokenType tt;
4880 JSParseNode *pn2;
4881 JSAtom *textAtom;
4882
4883 ts->flags &= ~TSF_XMLTAGMODE;
4884 for (;;) {
4885 ts->flags |= TSF_XMLTEXTMODE;
4886 tt = js_GetToken(cx, ts);
4887 ts->flags &= ~TSF_XMLTEXTMODE;
4888 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
4889
4890 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
4891 textAtom = CURRENT_TOKEN(ts).t_atom;
4892 if (textAtom) {
4893 /* Non-zero-length XML text scanned. */
4894 pn2 = XMLAtomNode(cx, ts, tc);
4895 if (!pn2)
4896 return JS_FALSE;
4897 pn->pn_pos.end = pn2->pn_pos.end;
4898 PN_APPEND(pn, pn2);
4899 }
4900
4901 ts->flags |= TSF_OPERAND;
4902 tt = js_GetToken(cx, ts);
4903 ts->flags &= ~TSF_OPERAND;
4904 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
4905 if (tt == TOK_XMLETAGO)
4906 break;
4907
4908 if (tt == TOK_LC) {
4909 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
4910 pn->pn_extra |= PNX_CANTFOLD;
4911 } else if (tt == TOK_XMLSTAGO) {
4912 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
4913 if (pn2) {
4914 pn2->pn_extra &= ~PNX_XMLROOT;
4915 pn->pn_extra |= pn2->pn_extra;
4916 }
4917 } else {
4918 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
4919 tt == TOK_XMLPI);
4920 pn2 = XMLAtomNode(cx, ts, tc);
4921 }
4922 if (!pn2)
4923 return JS_FALSE;
4924 pn->pn_pos.end = pn2->pn_pos.end;
4925 PN_APPEND(pn, pn2);
4926 }
4927
4928 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
4929 ts->flags |= TSF_XMLTAGMODE;
4930 return JS_TRUE;
4931 }
4932
4933 /*
4934 * Return a PN_LIST node containing an XML or XMLList Initialiser.
4935 */
4936 static JSParseNode *
XMLElementOrList(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowList)4937 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
4938 JSBool allowList)
4939 {
4940 JSParseNode *pn, *pn2, *list;
4941 JSBool hadSpace;
4942 JSTokenType tt;
4943 JSAtom *startAtom, *endAtom;
4944
4945 CHECK_RECURSION();
4946
4947 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
4948 pn = NewParseNode(cx, ts, PN_LIST, tc);
4949 if (!pn)
4950 return NULL;
4951
4952 ts->flags |= TSF_XMLTAGMODE;
4953 hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE);
4954 tt = js_GetToken(cx, ts);
4955 if (tt == TOK_ERROR)
4956 return NULL;
4957
4958 if (tt == TOK_XMLNAME || tt == TOK_LC) {
4959 /*
4960 * XMLElement. Append the tag and its contents, if any, to pn.
4961 */
4962 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
4963 if (!pn2)
4964 return NULL;
4965 js_MatchToken(cx, ts, TOK_XMLSPACE);
4966
4967 tt = js_GetToken(cx, ts);
4968 if (tt == TOK_XMLPTAGC) {
4969 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
4970 if (pn2->pn_type == TOK_XMLSTAGO) {
4971 PN_INIT_LIST(pn);
4972 RecycleTree(pn, tc);
4973 pn = pn2;
4974 } else {
4975 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
4976 pn2->pn_type == TOK_LC);
4977 PN_INIT_LIST_1(pn, pn2);
4978 if (!XML_FOLDABLE(pn2))
4979 pn->pn_extra |= PNX_CANTFOLD;
4980 }
4981 pn->pn_type = TOK_XMLPTAGC;
4982 pn->pn_extra |= PNX_XMLROOT;
4983 } else {
4984 /* We had better have a tag-close (>) at this point. */
4985 if (tt != TOK_XMLTAGC) {
4986 js_ReportCompileErrorNumber(cx, ts,
4987 JSREPORT_TS | JSREPORT_ERROR,
4988 JSMSG_BAD_XML_TAG_SYNTAX);
4989 return NULL;
4990 }
4991 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4992
4993 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
4994 if (pn2->pn_type != TOK_XMLSTAGO) {
4995 PN_INIT_LIST_1(pn, pn2);
4996 if (!XML_FOLDABLE(pn2))
4997 pn->pn_extra |= PNX_CANTFOLD;
4998 pn2 = pn;
4999 pn = NewParseNode(cx, ts, PN_LIST, tc);
5000 if (!pn)
5001 return NULL;
5002 }
5003
5004 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
5005 pn->pn_type = TOK_XMLELEM;
5006 PN_INIT_LIST_1(pn, pn2);
5007 if (!XML_FOLDABLE(pn2))
5008 pn->pn_extra |= PNX_CANTFOLD;
5009 pn->pn_extra |= PNX_XMLROOT;
5010
5011 /* Get element contents and delimiting end-tag-open sequence. */
5012 if (!XMLElementContent(cx, ts, pn, tc))
5013 return NULL;
5014
5015 js_MatchToken(cx, ts, TOK_XMLSPACE);
5016 tt = js_GetToken(cx, ts);
5017 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
5018 if (tt != TOK_XMLNAME && tt != TOK_LC) {
5019 js_ReportCompileErrorNumber(cx, ts,
5020 JSREPORT_TS | JSREPORT_ERROR,
5021 JSMSG_BAD_XML_TAG_SYNTAX);
5022 return NULL;
5023 }
5024
5025 /* Parse end tag; check mismatch at compile-time if we can. */
5026 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
5027 if (!pn2)
5028 return NULL;
5029 if (pn2->pn_type == TOK_XMLETAGO) {
5030 /* Oops, end tag has attributes! */
5031 js_ReportCompileErrorNumber(cx, ts,
5032 JSREPORT_TS | JSREPORT_ERROR,
5033 JSMSG_BAD_XML_TAG_SYNTAX);
5034 return NULL;
5035 }
5036 if (endAtom && startAtom && endAtom != startAtom) {
5037 JSString *str = ATOM_TO_STRING(startAtom);
5038
5039 /* End vs. start tag name mismatch: point to the tag name. */
5040 js_ReportCompileErrorNumberUC(cx, pn2,
5041 JSREPORT_PN | JSREPORT_ERROR,
5042 JSMSG_XML_TAG_NAME_MISMATCH,
5043 JSSTRING_CHARS(str));
5044 return NULL;
5045 }
5046
5047 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
5048 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
5049 list = NewParseNode(cx, ts, PN_LIST, tc);
5050 if (!list)
5051 return NULL;
5052 list->pn_type = TOK_XMLETAGO;
5053 PN_INIT_LIST_1(list, pn2);
5054 PN_APPEND(pn, list);
5055 if (!XML_FOLDABLE(pn2)) {
5056 list->pn_extra |= PNX_CANTFOLD;
5057 pn->pn_extra |= PNX_CANTFOLD;
5058 }
5059
5060 js_MatchToken(cx, ts, TOK_XMLSPACE);
5061 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
5062 }
5063
5064 /* Set pn_op now that pn has been updated to its final value. */
5065 pn->pn_op = JSOP_TOXML;
5066 } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) {
5067 /* XMLList Initialiser. */
5068 pn->pn_type = TOK_XMLLIST;
5069 pn->pn_op = JSOP_TOXMLLIST;
5070 PN_INIT_LIST(pn);
5071 pn->pn_extra |= PNX_XMLROOT;
5072 if (!XMLElementContent(cx, ts, pn, tc))
5073 return NULL;
5074
5075 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
5076 } else {
5077 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5078 JSMSG_BAD_XML_NAME_SYNTAX);
5079 return NULL;
5080 }
5081
5082 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5083 ts->flags &= ~TSF_XMLTAGMODE;
5084 return pn;
5085 }
5086
5087 static JSParseNode *
XMLElementOrListRoot(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowList)5088 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5089 JSBool allowList)
5090 {
5091 uint32 oldopts;
5092 JSParseNode *pn;
5093
5094 /*
5095 * Force XML support to be enabled so that comments and CDATA literals
5096 * are recognized, instead of <! followed by -- starting an HTML comment
5097 * to end of line (used in script tags to hide content from old browsers
5098 * that don't recognize <script>).
5099 */
5100 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
5101 pn = XMLElementOrList(cx, ts, tc, allowList);
5102 JS_SetOptions(cx, oldopts);
5103 return pn;
5104 }
5105
5106 JS_FRIEND_API(JSParseNode *)
js_ParseXMLTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts,JSBool allowList)5107 js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
5108 JSBool allowList)
5109 {
5110 JSStackFrame *fp, frame;
5111 JSParseNode *pn;
5112 JSTreeContext tc;
5113 JSTokenType tt;
5114
5115 /*
5116 * Push a compiler frame if we have no frames, or if the top frame is a
5117 * lightweight function activation, or if its scope chain doesn't match
5118 * the one passed to us.
5119 */
5120 fp = cx->fp;
5121 MaybeSetupFrame(cx, chain, fp, &frame);
5122 JS_KEEP_ATOMS(cx->runtime);
5123 TREE_CONTEXT_INIT(&tc);
5124
5125 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
5126 ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
5127 tt = js_GetToken(cx, ts);
5128 ts->flags &= ~TSF_OPERAND;
5129
5130 if (tt != TOK_XMLSTAGO) {
5131 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5132 JSMSG_BAD_XML_MARKUP);
5133 pn = NULL;
5134 } else {
5135 pn = XMLElementOrListRoot(cx, ts, &tc, allowList);
5136 }
5137
5138 ts->flags &= ~TSF_XMLONLYMODE;
5139 TREE_CONTEXT_FINISH(&tc);
5140 JS_UNKEEP_ATOMS(cx->runtime);
5141 cx->fp = fp;
5142 return pn;
5143 }
5144
5145 #endif /* JS_HAS_XMLSUPPORT */
5146
5147 static JSParseNode *
PrimaryExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSTokenType tt,JSBool afterDot)5148 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
5149 JSTokenType tt, JSBool afterDot)
5150 {
5151 JSParseNode *pn, *pn2, *pn3;
5152 JSOp op;
5153
5154 #if JS_HAS_SHARP_VARS
5155 JSParseNode *defsharp;
5156 JSBool notsharp;
5157
5158 defsharp = NULL;
5159 notsharp = JS_FALSE;
5160 again:
5161 /*
5162 * Control flows here after #n= is scanned. If the following primary is
5163 * not valid after such a "sharp variable" definition, the tt switch case
5164 * should set notsharp.
5165 */
5166 #endif
5167
5168 CHECK_RECURSION();
5169
5170 #if JS_HAS_GETTER_SETTER
5171 if (tt == TOK_NAME) {
5172 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
5173 if (tt == TOK_ERROR)
5174 return NULL;
5175 }
5176 #endif
5177
5178 switch (tt) {
5179 case TOK_FUNCTION:
5180 #if JS_HAS_XML_SUPPORT
5181 ts->flags |= TSF_KEYWORD_IS_NAME;
5182 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
5183 ts->flags &= ~TSF_KEYWORD_IS_NAME;
5184 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
5185 if (!pn2)
5186 return NULL;
5187 pn2->pn_type = TOK_FUNCTION;
5188 pn = QualifiedSuffix(cx, ts, pn2, tc);
5189 if (!pn)
5190 return NULL;
5191 break;
5192 }
5193 ts->flags &= ~TSF_KEYWORD_IS_NAME;
5194 #endif
5195 pn = FunctionExpr(cx, ts, tc);
5196 if (!pn)
5197 return NULL;
5198 break;
5199
5200 case TOK_LB:
5201 {
5202 JSBool matched;
5203 jsuint index;
5204
5205 pn = NewParseNode(cx, ts, PN_LIST, tc);
5206 if (!pn)
5207 return NULL;
5208 pn->pn_type = TOK_RB;
5209
5210 #if JS_HAS_SHARP_VARS
5211 if (defsharp) {
5212 PN_INIT_LIST_1(pn, defsharp);
5213 defsharp = NULL;
5214 } else
5215 #endif
5216 PN_INIT_LIST(pn);
5217
5218 ts->flags |= TSF_OPERAND;
5219 matched = js_MatchToken(cx, ts, TOK_RB);
5220 ts->flags &= ~TSF_OPERAND;
5221 if (!matched) {
5222 for (index = 0; ; index++) {
5223 if (index == ARRAY_INIT_LIMIT) {
5224 js_ReportCompileErrorNumber(cx, ts,
5225 JSREPORT_TS | JSREPORT_ERROR,
5226 JSMSG_ARRAY_INIT_TOO_BIG);
5227 return NULL;
5228 }
5229
5230 ts->flags |= TSF_OPERAND;
5231 tt = js_PeekToken(cx, ts);
5232 ts->flags &= ~TSF_OPERAND;
5233 if (tt == TOK_RB) {
5234 pn->pn_extra |= PNX_ENDCOMMA;
5235 break;
5236 }
5237
5238 if (tt == TOK_COMMA) {
5239 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
5240 js_MatchToken(cx, ts, TOK_COMMA);
5241 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
5242 } else {
5243 pn2 = AssignExpr(cx, ts, tc);
5244 }
5245 if (!pn2)
5246 return NULL;
5247 PN_APPEND(pn, pn2);
5248
5249 if (tt != TOK_COMMA) {
5250 /* If we didn't already match TOK_COMMA in above case. */
5251 if (!js_MatchToken(cx, ts, TOK_COMMA))
5252 break;
5253 }
5254 }
5255
5256 #if JS_HAS_GENERATORS
5257 /*
5258 * At this point, (index == 0 && pn->pn_count != 0) implies one
5259 * element initialiser was parsed (possibly with a defsharp before
5260 * the left bracket).
5261 *
5262 * An array comprehension of the form:
5263 *
5264 * [i * j for (i in o) for (j in p) if (i != j)]
5265 *
5266 * translates to roughly the following let expression:
5267 *
5268 * let (array = new Array, i, j) {
5269 * for (i in o) let {
5270 * for (j in p)
5271 * if (i != j)
5272 * array.push(i * j)
5273 * }
5274 * array
5275 * }
5276 *
5277 * where array is a nameless block-local variable. The "roughly"
5278 * means that an implementation may optimize away the array.push.
5279 * An array comprehension opens exactly one block scope, no matter
5280 * how many for heads it contains.
5281 *
5282 * Each let () {...} or for (let ...) ... compiles to:
5283 *
5284 * JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
5285 *
5286 * where <o> is a literal object representing the block scope,
5287 * with <n> properties, naming each var declared in the block.
5288 *
5289 * Each var declaration in a let-block binds a name in <o> at
5290 * compile time, and allocates a slot on the operand stack at
5291 * runtime via JSOP_ENTERBLOCK. A block-local var is accessed
5292 * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
5293 * JSOP_FORLOCAL. These ops all have an immediate operand, the
5294 * local slot's stack index from fp->spbase.
5295 *
5296 * The array comprehension iteration step, array.push(i * j) in
5297 * the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
5298 * where <array> is the index of array's stack slot.
5299 */
5300 if (index == 0 &&
5301 pn->pn_count != 0 &&
5302 js_MatchToken(cx, ts, TOK_FOR)) {
5303 JSParseNode **pnp, *pnexp, *pntop, *pnlet;
5304 BindData data;
5305 JSRuntime *rt;
5306 JSStmtInfo stmtInfo;
5307 JSAtom *atom;
5308
5309 /* Relabel pn as an array comprehension node. */
5310 pn->pn_type = TOK_ARRAYCOMP;
5311
5312 /*
5313 * Remove the comprehension expression from pn's linked list
5314 * and save it via pnexp. We'll re-install it underneath the
5315 * ARRAYPUSH node after we parse the rest of the comprehension.
5316 */
5317 pnexp = PN_LAST(pn);
5318 JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
5319 pn->pn_tail = (--pn->pn_count == 1)
5320 ? &pn->pn_head->pn_next
5321 : &pn->pn_head;
5322 *pn->pn_tail = NULL;
5323
5324 /*
5325 * Make a parse-node and literal object representing the array
5326 * comprehension's block scope.
5327 */
5328 pntop = PushLexicalScope(cx, ts, tc, &stmtInfo);
5329 if (!pntop)
5330 return NULL;
5331 pnp = &pntop->pn_expr;
5332
5333 data.pn = NULL;
5334 data.ts = ts;
5335 data.obj = tc->blockChain;
5336 data.op = JSOP_NOP;
5337 data.binder = BindLet;
5338 data.u.let.index = 0;
5339 data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG;
5340
5341 rt = cx->runtime;
5342 do {
5343 /*
5344 * FOR node is binary, left is control and right is body.
5345 * Use index to count each block-local let-variable on the
5346 * left-hand side of IN.
5347 */
5348 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
5349 if (!pn2)
5350 return NULL;
5351
5352 pn2->pn_op = JSOP_FORIN;
5353 if (js_MatchToken(cx, ts, TOK_NAME)) {
5354 if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom)
5355 pn2->pn_op = JSOP_FOREACH;
5356 else
5357 js_UngetToken(ts);
5358 }
5359 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
5360
5361 tt = js_GetToken(cx, ts);
5362 switch (tt) {
5363 #if JS_HAS_DESTRUCTURING
5364 case TOK_LB:
5365 case TOK_LC:
5366 pnlet = DestructuringExpr(cx, &data, tc, tt);
5367 if (!pnlet)
5368 return NULL;
5369
5370 if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) {
5371 js_ReportCompileErrorNumber(cx, ts,
5372 JSREPORT_TS |
5373 JSREPORT_ERROR,
5374 JSMSG_BAD_FOR_LEFTSIDE);
5375 return NULL;
5376 }
5377
5378 /* Destructuring requires [key, value] enumeration. */
5379 if (pn2->pn_op != JSOP_FOREACH)
5380 pn2->pn_op = JSOP_FOREACHKEYVAL;
5381 break;
5382 #endif
5383
5384 case TOK_NAME:
5385 atom = CURRENT_TOKEN(ts).t_atom;
5386 if (!data.binder(cx, &data, atom, tc))
5387 return NULL;
5388
5389 /*
5390 * Create a name node with op JSOP_NAME. We can't set
5391 * op to JSOP_GETLOCAL here, because we don't yet know
5392 * the block's depth in the operand stack frame. The
5393 * code generator computes that, and it tries to bind
5394 * all names to slots, so we must let it do the deed.
5395 */
5396 pnlet = NewParseNode(cx, ts, PN_NAME, tc);
5397 if (!pnlet)
5398 return NULL;
5399 pnlet->pn_op = JSOP_NAME;
5400 pnlet->pn_atom = atom;
5401 pnlet->pn_expr = NULL;
5402 pnlet->pn_slot = -1;
5403 pnlet->pn_attrs = 0;
5404 break;
5405
5406 default:
5407 js_ReportCompileErrorNumber(cx, ts,
5408 JSREPORT_TS|JSREPORT_ERROR,
5409 JSMSG_NO_VARIABLE_NAME);
5410 return NULL;
5411 }
5412
5413 MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
5414 pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet,
5415 Expr(cx, ts, tc), tc);
5416 if (!pn3)
5417 return NULL;
5418
5419 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
5420 pn2->pn_left = pn3;
5421 *pnp = pn2;
5422 pnp = &pn2->pn_right;
5423 } while (js_MatchToken(cx, ts, TOK_FOR));
5424
5425 if (js_MatchToken(cx, ts, TOK_IF)) {
5426 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
5427 if (!pn2)
5428 return NULL;
5429 pn2->pn_kid1 = Condition(cx, ts, tc);
5430 if (!pn2->pn_kid1)
5431 return NULL;
5432 pn2->pn_kid2 = NULL;
5433 pn2->pn_kid3 = NULL;
5434 *pnp = pn2;
5435 pnp = &pn2->pn_kid2;
5436 }
5437
5438 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
5439 if (!pn2)
5440 return NULL;
5441 pn2->pn_type = TOK_ARRAYPUSH;
5442 pn2->pn_op = JSOP_ARRAYPUSH;
5443 pn2->pn_kid = pnexp;
5444 *pnp = pn2;
5445 PN_APPEND(pn, pntop);
5446
5447 js_PopStatement(tc);
5448 }
5449 #endif /* JS_HAS_GENERATORS */
5450
5451 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
5452 }
5453 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5454 return pn;
5455 }
5456
5457 #if JS_HAS_BLOCK_SCOPE
5458 case TOK_LET:
5459 pn = LetBlock(cx, ts, tc, JS_FALSE);
5460 if (!pn)
5461 return NULL;
5462 break;
5463 #endif
5464
5465 case TOK_LC:
5466 {
5467 JSBool afterComma;
5468
5469 pn = NewParseNode(cx, ts, PN_LIST, tc);
5470 if (!pn)
5471 return NULL;
5472 pn->pn_type = TOK_RC;
5473
5474 #if JS_HAS_SHARP_VARS
5475 if (defsharp) {
5476 PN_INIT_LIST_1(pn, defsharp);
5477 defsharp = NULL;
5478 } else
5479 #endif
5480 PN_INIT_LIST(pn);
5481
5482 afterComma = JS_FALSE;
5483 for (;;) {
5484 ts->flags |= TSF_KEYWORD_IS_NAME;
5485 tt = js_GetToken(cx, ts);
5486 ts->flags &= ~TSF_KEYWORD_IS_NAME;
5487 switch (tt) {
5488 case TOK_NUMBER:
5489 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
5490 if (pn3)
5491 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
5492 break;
5493 case TOK_NAME:
5494 #if JS_HAS_GETTER_SETTER
5495 {
5496 JSAtom *atom;
5497 JSRuntime *rt;
5498
5499 atom = CURRENT_TOKEN(ts).t_atom;
5500 rt = cx->runtime;
5501 if (atom == rt->atomState.getAtom ||
5502 atom == rt->atomState.setAtom) {
5503 op = (atom == rt->atomState.getAtom)
5504 ? JSOP_GETTER
5505 : JSOP_SETTER;
5506 if (js_MatchToken(cx, ts, TOK_NAME)) {
5507 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
5508 if (!pn3)
5509 return NULL;
5510 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
5511 pn3->pn_expr = NULL;
5512 pn3->pn_slot = -1;
5513 pn3->pn_attrs = 0;
5514
5515 /* We have to fake a 'function' token here. */
5516 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
5517 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
5518 pn2 = FunctionExpr(cx, ts, tc);
5519 pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
5520 goto skip;
5521 }
5522 }
5523 /* else fall thru ... */
5524 }
5525 #endif
5526 case TOK_STRING:
5527 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
5528 if (pn3)
5529 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
5530 break;
5531 case TOK_RC:
5532 if (afterComma &&
5533 !js_ReportCompileErrorNumber(cx, ts,
5534 JSREPORT_TS |
5535 JSREPORT_WARNING |
5536 JSREPORT_STRICT,
5537 JSMSG_TRAILING_COMMA)) {
5538 return NULL;
5539 }
5540 goto end_obj_init;
5541 default:
5542 js_ReportCompileErrorNumber(cx, ts,
5543 JSREPORT_TS | JSREPORT_ERROR,
5544 JSMSG_BAD_PROP_ID);
5545 return NULL;
5546 }
5547
5548 tt = js_GetToken(cx, ts);
5549 #if JS_HAS_GETTER_SETTER
5550 if (tt == TOK_NAME) {
5551 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
5552 if (tt == TOK_ERROR)
5553 return NULL;
5554 }
5555 #endif
5556 if (tt != TOK_COLON) {
5557 js_ReportCompileErrorNumber(cx, ts,
5558 JSREPORT_TS | JSREPORT_ERROR,
5559 JSMSG_COLON_AFTER_ID);
5560 return NULL;
5561 }
5562 op = CURRENT_TOKEN(ts).t_op;
5563 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc);
5564 #if JS_HAS_GETTER_SETTER
5565 skip:
5566 #endif
5567 if (!pn2)
5568 return NULL;
5569 PN_APPEND(pn, pn2);
5570
5571 tt = js_GetToken(cx, ts);
5572 if (tt == TOK_RC)
5573 goto end_obj_init;
5574 if (tt != TOK_COMMA) {
5575 js_ReportCompileErrorNumber(cx, ts,
5576 JSREPORT_TS | JSREPORT_ERROR,
5577 JSMSG_CURLY_AFTER_LIST);
5578 return NULL;
5579 }
5580 afterComma = JS_TRUE;
5581 }
5582 end_obj_init:
5583 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5584 return pn;
5585 }
5586
5587 #if JS_HAS_SHARP_VARS
5588 case TOK_DEFSHARP:
5589 if (defsharp)
5590 goto badsharp;
5591 defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
5592 if (!defsharp)
5593 return NULL;
5594 defsharp->pn_kid = NULL;
5595 defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
5596 ts->flags |= TSF_OPERAND;
5597 tt = js_GetToken(cx, ts);
5598 ts->flags &= ~TSF_OPERAND;
5599 goto again;
5600
5601 case TOK_USESHARP:
5602 /* Check for forward/dangling references at runtime, to allow eval. */
5603 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5604 if (!pn)
5605 return NULL;
5606 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
5607 notsharp = JS_TRUE;
5608 break;
5609 #endif /* JS_HAS_SHARP_VARS */
5610
5611 case TOK_LP:
5612 pn = NewParseNode(cx, ts, PN_UNARY, tc);
5613 if (!pn)
5614 return NULL;
5615 pn2 = BracketedExpr(cx, ts, tc);
5616 if (!pn2)
5617 return NULL;
5618
5619 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
5620 if (pn2->pn_type == TOK_RP ||
5621 (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec &&
5622 !afterDot)) {
5623 /*
5624 * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly
5625 * to help the decompiler look ahead from a JSOP_ENDINIT to see a
5626 * JSOP_GROUP followed by a POP or POPV. That sequence means the
5627 * parentheses are mandatory, to disambiguate object initialisers
5628 * as expression statements from block statements.
5629 *
5630 * Also drop pn if pn2 is a member or a primary expression of any
5631 * kind. This is required to avoid generating a JSOP_GROUP that
5632 * will null the |obj| interpreter register, causing |this| in any
5633 * call of that member expression to bind to the global object.
5634 */
5635 pn->pn_kid = NULL;
5636 RecycleTree(pn, tc);
5637 pn = pn2;
5638 } else {
5639 pn->pn_type = TOK_RP;
5640 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
5641 pn->pn_kid = pn2;
5642 }
5643 break;
5644
5645 #if JS_HAS_XML_SUPPORT
5646 case TOK_STAR:
5647 pn = QualifiedIdentifier(cx, ts, tc);
5648 if (!pn)
5649 return NULL;
5650 notsharp = JS_TRUE;
5651 break;
5652
5653 case TOK_AT:
5654 pn = AttributeIdentifier(cx, ts, tc);
5655 if (!pn)
5656 return NULL;
5657 notsharp = JS_TRUE;
5658 break;
5659
5660 case TOK_XMLSTAGO:
5661 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
5662 if (!pn)
5663 return NULL;
5664 notsharp = JS_TRUE; /* XXXbe could be sharp? */
5665 break;
5666 #endif /* JS_HAS_XML_SUPPORT */
5667
5668 case TOK_STRING:
5669 #if JS_HAS_SHARP_VARS
5670 notsharp = JS_TRUE;
5671 /* FALL THROUGH */
5672 #endif
5673
5674 #if JS_HAS_XML_SUPPORT
5675 case TOK_XMLCDATA:
5676 case TOK_XMLCOMMENT:
5677 case TOK_XMLPI:
5678 #endif
5679 case TOK_NAME:
5680 case TOK_OBJECT:
5681 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5682 if (!pn)
5683 return NULL;
5684 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
5685 #if JS_HAS_XML_SUPPORT
5686 if (tt == TOK_XMLPI)
5687 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
5688 else
5689 #endif
5690 pn->pn_op = CURRENT_TOKEN(ts).t_op;
5691 if (tt == TOK_NAME) {
5692 pn->pn_arity = PN_NAME;
5693 pn->pn_expr = NULL;
5694 pn->pn_slot = -1;
5695 pn->pn_attrs = 0;
5696
5697 #if JS_HAS_XML_SUPPORT
5698 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
5699 if (afterDot) {
5700 JSString *str;
5701
5702 /*
5703 * Here PrimaryExpr is called after '.' or '..' and we
5704 * just scanned .name:: or ..name:: . This is the only
5705 * case where a keyword after '.' or '..' is not
5706 * treated as a property name.
5707 */
5708 str = ATOM_TO_STRING(pn->pn_atom);
5709 tt = js_CheckKeyword(JSSTRING_CHARS(str),
5710 JSSTRING_LENGTH(str));
5711 if (tt == TOK_FUNCTION) {
5712 pn->pn_arity = PN_NULLARY;
5713 pn->pn_type = TOK_FUNCTION;
5714 } else if (tt != TOK_EOF) {
5715 js_ReportCompileErrorNumber(
5716 cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5717 JSMSG_KEYWORD_NOT_NS);
5718 return NULL;
5719 }
5720 }
5721 pn = QualifiedSuffix(cx, ts, pn, tc);
5722 if (!pn)
5723 return NULL;
5724 break;
5725 }
5726 #endif
5727
5728 /* Unqualified __parent__ and __proto__ uses require activations. */
5729 if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
5730 pn->pn_atom == cx->runtime->atomState.protoAtom) {
5731 tc->flags |= TCF_FUN_HEAVYWEIGHT;
5732 } else {
5733 JSAtomListElement *ale;
5734 JSStackFrame *fp;
5735 JSBool loopy;
5736
5737 /* Measure optimizable global variable uses. */
5738 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
5739 if (ale &&
5740 !(fp = cx->fp)->fun &&
5741 fp->scopeChain == fp->varobj &&
5742 js_IsGlobalReference(tc, pn->pn_atom, &loopy)) {
5743 tc->globalUses++;
5744 if (loopy)
5745 tc->loopyGlobalUses++;
5746 }
5747 }
5748 }
5749 break;
5750
5751 case TOK_NUMBER:
5752 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5753 if (!pn)
5754 return NULL;
5755 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
5756 #if JS_HAS_SHARP_VARS
5757 notsharp = JS_TRUE;
5758 #endif
5759 break;
5760
5761 case TOK_PRIMARY:
5762 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
5763 if (!pn)
5764 return NULL;
5765 pn->pn_op = CURRENT_TOKEN(ts).t_op;
5766 #if JS_HAS_SHARP_VARS
5767 notsharp = JS_TRUE;
5768 #endif
5769 break;
5770
5771 #if !JS_HAS_EXPORT_IMPORT
5772 case TOK_EXPORT:
5773 case TOK_IMPORT:
5774 #endif
5775 case TOK_ERROR:
5776 /* The scanner or one of its subroutines reported the error. */
5777 return NULL;
5778
5779 default:
5780 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5781 JSMSG_SYNTAX_ERROR);
5782 return NULL;
5783 }
5784
5785 #if JS_HAS_SHARP_VARS
5786 if (defsharp) {
5787 if (notsharp) {
5788 badsharp:
5789 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
5790 JSMSG_BAD_SHARP_VAR_DEF);
5791 return NULL;
5792 }
5793 defsharp->pn_kid = pn;
5794 return defsharp;
5795 }
5796 #endif
5797 return pn;
5798 }
5799
5800 /*
5801 * Fold from one constant type to another.
5802 * XXX handles only strings and numbers for now
5803 */
5804 static JSBool
FoldType(JSContext * cx,JSParseNode * pn,JSTokenType type)5805 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
5806 {
5807 if (pn->pn_type != type) {
5808 switch (type) {
5809 case TOK_NUMBER:
5810 if (pn->pn_type == TOK_STRING) {
5811 jsdouble d;
5812 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
5813 return JS_FALSE;
5814 pn->pn_dval = d;
5815 pn->pn_type = TOK_NUMBER;
5816 pn->pn_op = JSOP_NUMBER;
5817 }
5818 break;
5819
5820 case TOK_STRING:
5821 if (pn->pn_type == TOK_NUMBER) {
5822 JSString *str = js_NumberToString(cx, pn->pn_dval);
5823 if (!str)
5824 return JS_FALSE;
5825 pn->pn_atom = js_AtomizeString(cx, str, 0);
5826 if (!pn->pn_atom)
5827 return JS_FALSE;
5828 pn->pn_type = TOK_STRING;
5829 pn->pn_op = JSOP_STRING;
5830 }
5831 break;
5832
5833 default:;
5834 }
5835 }
5836 return JS_TRUE;
5837 }
5838
5839 /*
5840 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
5841 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
5842 * a successful call to this function.
5843 */
5844 static JSBool
FoldBinaryNumeric(JSContext * cx,JSOp op,JSParseNode * pn1,JSParseNode * pn2,JSParseNode * pn,JSTreeContext * tc)5845 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
5846 JSParseNode *pn, JSTreeContext *tc)
5847 {
5848 jsdouble d, d2;
5849 int32 i, j;
5850 uint32 u;
5851
5852 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
5853 d = pn1->pn_dval;
5854 d2 = pn2->pn_dval;
5855 switch (op) {
5856 case JSOP_LSH:
5857 case JSOP_RSH:
5858 if (!js_DoubleToECMAInt32(cx, d, &i))
5859 return JS_FALSE;
5860 if (!js_DoubleToECMAInt32(cx, d2, &j))
5861 return JS_FALSE;
5862 j &= 31;
5863 d = (op == JSOP_LSH) ? i << j : i >> j;
5864 break;
5865
5866 case JSOP_URSH:
5867 if (!js_DoubleToECMAUint32(cx, d, &u))
5868 return JS_FALSE;
5869 if (!js_DoubleToECMAInt32(cx, d2, &j))
5870 return JS_FALSE;
5871 j &= 31;
5872 d = u >> j;
5873 break;
5874
5875 case JSOP_ADD:
5876 d += d2;
5877 break;
5878
5879 case JSOP_SUB:
5880 d -= d2;
5881 break;
5882
5883 case JSOP_MUL:
5884 d *= d2;
5885 break;
5886
5887 case JSOP_DIV:
5888 if (d2 == 0) {
5889 #if defined(XP_WIN)
5890 /* XXX MSVC miscompiles such that (NaN == 0) */
5891 if (JSDOUBLE_IS_NaN(d2))
5892 d = *cx->runtime->jsNaN;
5893 else
5894 #endif
5895 if (d == 0 || JSDOUBLE_IS_NaN(d))
5896 d = *cx->runtime->jsNaN;
5897 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
5898 d = *cx->runtime->jsNegativeInfinity;
5899 else
5900 d = *cx->runtime->jsPositiveInfinity;
5901 } else {
5902 d /= d2;
5903 }
5904 break;
5905
5906 case JSOP_MOD:
5907 if (d2 == 0) {
5908 d = *cx->runtime->jsNaN;
5909 } else {
5910 #if defined(XP_WIN)
5911 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
5912 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
5913 #endif
5914 d = fmod(d, d2);
5915 }
5916 break;
5917
5918 default:;
5919 }
5920
5921 /* Take care to allow pn1 or pn2 to alias pn. */
5922 if (pn1 != pn)
5923 RecycleTree(pn1, tc);
5924 if (pn2 != pn)
5925 RecycleTree(pn2, tc);
5926 pn->pn_type = TOK_NUMBER;
5927 pn->pn_op = JSOP_NUMBER;
5928 pn->pn_arity = PN_NULLARY;
5929 pn->pn_dval = d;
5930 return JS_TRUE;
5931 }
5932
5933 #if JS_HAS_XML_SUPPORT
5934
5935 static JSBool
FoldXMLConstants(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)5936 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
5937 {
5938 JSTokenType tt;
5939 JSParseNode **pnp, *pn1, *pn2;
5940 JSString *accum, *str;
5941 uint32 i, j;
5942
5943 JS_ASSERT(pn->pn_arity == PN_LIST);
5944 tt = pn->pn_type;
5945 pnp = &pn->pn_head;
5946 pn1 = *pnp;
5947 accum = NULL;
5948 if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
5949 if (tt == TOK_XMLETAGO)
5950 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
5951 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
5952 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
5953 }
5954
5955 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
5956 /* The parser already rejected end-tags with attributes. */
5957 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
5958 switch (pn2->pn_type) {
5959 case TOK_XMLATTR:
5960 if (!accum)
5961 goto cantfold;
5962 /* FALL THROUGH */
5963 case TOK_XMLNAME:
5964 case TOK_XMLSPACE:
5965 case TOK_XMLTEXT:
5966 case TOK_STRING:
5967 if (pn2->pn_arity == PN_LIST)
5968 goto cantfold;
5969 str = ATOM_TO_STRING(pn2->pn_atom);
5970 break;
5971
5972 case TOK_XMLCDATA:
5973 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
5974 if (!str)
5975 return JS_FALSE;
5976 break;
5977
5978 case TOK_XMLCOMMENT:
5979 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
5980 if (!str)
5981 return JS_FALSE;
5982 break;
5983
5984 case TOK_XMLPI:
5985 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
5986 ATOM_TO_STRING(pn2->pn_atom2));
5987 if (!str)
5988 return JS_FALSE;
5989 break;
5990
5991 cantfold:
5992 default:
5993 JS_ASSERT(*pnp == pn1);
5994 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
5995 (i & 1) ^ (j & 1)) {
5996 #ifdef DEBUG_brendanXXX
5997 printf("1: %d, %d => %s\n",
5998 i, j, accum ? JS_GetStringBytes(accum) : "NULL");
5999 #endif
6000 } else if (accum && pn1 != pn2) {
6001 while (pn1->pn_next != pn2) {
6002 pn1 = RecycleTree(pn1, tc);
6003 --pn->pn_count;
6004 }
6005 pn1->pn_type = TOK_XMLTEXT;
6006 pn1->pn_op = JSOP_STRING;
6007 pn1->pn_arity = PN_NULLARY;
6008 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
6009 if (!pn1->pn_atom)
6010 return JS_FALSE;
6011 JS_ASSERT(pnp != &pn1->pn_next);
6012 *pnp = pn1;
6013 }
6014 pnp = &pn2->pn_next;
6015 pn1 = *pnp;
6016 accum = NULL;
6017 continue;
6018 }
6019
6020 if (accum) {
6021 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
6022 ? js_AddAttributePart(cx, i & 1, accum, str)
6023 : js_ConcatStrings(cx, accum, str);
6024 if (!str)
6025 return JS_FALSE;
6026 #ifdef DEBUG_brendanXXX
6027 printf("2: %d, %d => %s (%u)\n",
6028 i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str));
6029 #endif
6030 ++j;
6031 }
6032 accum = str;
6033 }
6034
6035 if (accum) {
6036 str = NULL;
6037 if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
6038 if (tt == TOK_XMLPTAGC)
6039 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
6040 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
6041 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
6042 }
6043 if (str) {
6044 accum = js_ConcatStrings(cx, accum, str);
6045 if (!accum)
6046 return JS_FALSE;
6047 }
6048
6049 JS_ASSERT(*pnp == pn1);
6050 while (pn1->pn_next) {
6051 pn1 = RecycleTree(pn1, tc);
6052 --pn->pn_count;
6053 }
6054 pn1->pn_type = TOK_XMLTEXT;
6055 pn1->pn_op = JSOP_STRING;
6056 pn1->pn_arity = PN_NULLARY;
6057 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
6058 if (!pn1->pn_atom)
6059 return JS_FALSE;
6060 JS_ASSERT(pnp != &pn1->pn_next);
6061 *pnp = pn1;
6062 }
6063
6064 if (pn1 && pn->pn_count == 1) {
6065 /*
6066 * Only one node under pn, and it has been folded: move pn1 onto pn
6067 * unless pn is an XML root (in which case we need it to tell the code
6068 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
6069 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
6070 * extra "<" and "/>" bracketing at runtime.
6071 */
6072 if (!(pn->pn_extra & PNX_XMLROOT)) {
6073 PN_MOVE_NODE(pn, pn1);
6074 } else if (tt == TOK_XMLPTAGC) {
6075 pn->pn_type = TOK_XMLELEM;
6076 pn->pn_op = JSOP_TOXML;
6077 }
6078 }
6079 return JS_TRUE;
6080 }
6081
6082 #endif /* JS_HAS_XML_SUPPORT */
6083
6084 static JSBool
StartsWith(JSParseNode * pn,JSTokenType tt)6085 StartsWith(JSParseNode *pn, JSTokenType tt)
6086 {
6087 #define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO
6088
6089 recur:
6090 if (pn->pn_type == tt)
6091 return JS_TRUE;
6092 switch (pn->pn_arity) {
6093 case PN_FUNC:
6094 return tt == TOK_FUNCTION;
6095 case PN_LIST:
6096 if (pn->pn_head)
6097 TAIL_RECURSE(pn->pn_head);
6098 break;
6099 case PN_TERNARY:
6100 if (pn->pn_kid1)
6101 TAIL_RECURSE(pn->pn_kid1);
6102 break;
6103 case PN_BINARY:
6104 if (pn->pn_left)
6105 TAIL_RECURSE(pn->pn_left);
6106 break;
6107 case PN_UNARY:
6108 /* A parenthesized expression starts with a left parenthesis. */
6109 if (pn->pn_type == TOK_RP)
6110 return tt == TOK_LP;
6111 if (pn->pn_kid)
6112 TAIL_RECURSE(pn->pn_kid);
6113 break;
6114 case PN_NAME:
6115 if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT)
6116 TAIL_RECURSE(pn->pn_expr);
6117 /* FALL THROUGH */
6118 }
6119 return JS_FALSE;
6120 #undef TAIL_RECURSE
6121 }
6122
6123 JSBool
js_FoldConstants(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)6124 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
6125 {
6126 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
6127 int stackDummy;
6128
6129 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
6130 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
6131 return JS_FALSE;
6132 }
6133
6134 switch (pn->pn_arity) {
6135 case PN_FUNC:
6136 {
6137 uint16 oldflags = tc->flags;
6138
6139 tc->flags = (uint16) pn->pn_flags;
6140 if (!js_FoldConstants(cx, pn->pn_body, tc))
6141 return JS_FALSE;
6142 tc->flags = oldflags;
6143 break;
6144 }
6145
6146 case PN_LIST:
6147 #if 0 /* JS_HAS_XML_SUPPORT */
6148 switch (pn->pn_type) {
6149 case TOK_XMLELEM:
6150 case TOK_XMLLIST:
6151 case TOK_XMLPTAGC:
6152 /*
6153 * Try to fold this XML parse tree once, from the top down, into
6154 * a JSXML tree with just one object wrapping the tree root.
6155 *
6156 * Certain subtrees could be folded similarly, but we'd have to
6157 * ensure that none used namespace prefixes declared elsewhere in
6158 * its super-tree, and we would have to convert each XML object
6159 * created at runtime for such sub-trees back into a string, and
6160 * concatenate and re-parse anyway.
6161 */
6162 if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT &&
6163 !(tc->flags & TCF_HAS_DEFXMLNS)) {
6164 JSObject *obj;
6165 JSAtom *atom;
6166
6167 obj = js_ParseNodeToXMLObject(cx, pn);
6168 if (!obj)
6169 return JS_FALSE;
6170 atom = js_AtomizeObject(cx, obj, 0);
6171 if (!atom)
6172 return JS_FALSE;
6173 pn->pn_op = JSOP_XMLOBJECT;
6174 pn->pn_arity = PN_NULLARY;
6175 pn->pn_atom = atom;
6176 return JS_TRUE;
6177 }
6178
6179 /*
6180 * Can't fold from parse node to XML tree -- try folding strings
6181 * as much as possible, and folding XML sub-trees bottom up to
6182 * minimize string concatenation and ToXML/ToXMLList operations
6183 * at runtime.
6184 */
6185 break;
6186
6187 default:;
6188 }
6189 #endif
6190
6191 /* Save the list head in pn1 for later use. */
6192 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6193 if (!js_FoldConstants(cx, pn2, tc))
6194 return JS_FALSE;
6195 }
6196 break;
6197
6198 case PN_TERNARY:
6199 /* Any kid may be null (e.g. for (;;)). */
6200 pn1 = pn->pn_kid1;
6201 pn2 = pn->pn_kid2;
6202 pn3 = pn->pn_kid3;
6203 if (pn1 && !js_FoldConstants(cx, pn1, tc))
6204 return JS_FALSE;
6205 if (pn2 && !js_FoldConstants(cx, pn2, tc))
6206 return JS_FALSE;
6207 if (pn3 && !js_FoldConstants(cx, pn3, tc))
6208 return JS_FALSE;
6209 break;
6210
6211 case PN_BINARY:
6212 /* First kid may be null (for default case in switch). */
6213 pn1 = pn->pn_left;
6214 pn2 = pn->pn_right;
6215 if (pn1 && !js_FoldConstants(cx, pn1, tc))
6216 return JS_FALSE;
6217 if (!js_FoldConstants(cx, pn2, tc))
6218 return JS_FALSE;
6219 break;
6220
6221 case PN_UNARY:
6222 /* Our kid may be null (e.g. return; vs. return e;). */
6223 pn1 = pn->pn_kid;
6224 if (pn1 && !js_FoldConstants(cx, pn1, tc))
6225 return JS_FALSE;
6226 break;
6227
6228 case PN_NAME:
6229 /*
6230 * Skip pn1 down along a chain of dotted member expressions to avoid
6231 * excessive recursion. Our only goal here is to fold constants (if
6232 * any) in the primary expression operand to the left of the first
6233 * dot in the chain.
6234 */
6235 pn1 = pn->pn_expr;
6236 while (pn1 && pn1->pn_arity == PN_NAME)
6237 pn1 = pn1->pn_expr;
6238 if (pn1 && !js_FoldConstants(cx, pn1, tc))
6239 return JS_FALSE;
6240 break;
6241
6242 case PN_NULLARY:
6243 break;
6244 }
6245
6246 switch (pn->pn_type) {
6247 case TOK_IF:
6248 if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
6249 break;
6250 /* FALL THROUGH */
6251
6252 case TOK_HOOK:
6253 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
6254 while (pn1->pn_type == TOK_RP)
6255 pn1 = pn1->pn_kid;
6256 switch (pn1->pn_type) {
6257 case TOK_NUMBER:
6258 if (pn1->pn_dval == 0)
6259 pn2 = pn3;
6260 break;
6261 case TOK_STRING:
6262 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
6263 pn2 = pn3;
6264 break;
6265 case TOK_PRIMARY:
6266 if (pn1->pn_op == JSOP_TRUE)
6267 break;
6268 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
6269 pn2 = pn3;
6270 break;
6271 }
6272 /* FALL THROUGH */
6273 default:
6274 /* Early return to dodge common code that copies pn2 to pn. */
6275 return JS_TRUE;
6276 }
6277
6278 if (pn2) {
6279 /*
6280 * pn2 is the then- or else-statement subtree to compile. Take
6281 * care not to expose an object initialiser, which would be parsed
6282 * as a block, to the Statement parser via eval(uneval(e)) where e
6283 * is '1 ? {p:2, q:3}[i] : r;' or the like.
6284 */
6285 if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) {
6286 pn->pn_type = TOK_RP;
6287 pn->pn_arity = PN_UNARY;
6288 pn->pn_kid = pn2;
6289 } else {
6290 PN_MOVE_NODE(pn, pn2);
6291 }
6292 }
6293 if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) {
6294 /*
6295 * False condition and no else, or an empty then-statement was
6296 * moved up over pn. Either way, make pn an empty block (not an
6297 * empty statement, which does not decompile, even when labeled).
6298 * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid
6299 * or an empty statement for a child.
6300 */
6301 pn->pn_type = TOK_LC;
6302 pn->pn_arity = PN_LIST;
6303 PN_INIT_LIST(pn);
6304 }
6305 RecycleTree(pn2, tc);
6306 if (pn3 && pn3 != pn2)
6307 RecycleTree(pn3, tc);
6308 break;
6309
6310 case TOK_ASSIGN:
6311 /*
6312 * Compound operators such as *= should be subject to folding, in case
6313 * the left-hand side is constant, and so that the decompiler produces
6314 * the same string that you get from decompiling a script or function
6315 * compiled from that same string. As with +, += is special.
6316 */
6317 if (pn->pn_op == JSOP_NOP)
6318 break;
6319 if (pn->pn_op != JSOP_ADD)
6320 goto do_binary_op;
6321 /* FALL THROUGH */
6322
6323 case TOK_PLUS:
6324 if (pn->pn_arity == PN_LIST) {
6325 size_t length, length2;
6326 jschar *chars;
6327 JSString *str, *str2;
6328
6329 /*
6330 * Any string literal term with all others number or string means
6331 * this is a concatenation. If any term is not a string or number
6332 * literal, we can't fold.
6333 */
6334 JS_ASSERT(pn->pn_count > 2);
6335 if (pn->pn_extra & PNX_CANTFOLD)
6336 return JS_TRUE;
6337 if (pn->pn_extra != PNX_STRCAT)
6338 goto do_binary_op;
6339
6340 /* Ok, we're concatenating: convert non-string constant operands. */
6341 length = 0;
6342 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6343 if (!FoldType(cx, pn2, TOK_STRING))
6344 return JS_FALSE;
6345 /* XXX fold only if all operands convert to string */
6346 if (pn2->pn_type != TOK_STRING)
6347 return JS_TRUE;
6348 length += ATOM_TO_STRING(pn2->pn_atom)->length;
6349 }
6350
6351 /* Allocate a new buffer and string descriptor for the result. */
6352 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
6353 if (!chars)
6354 return JS_FALSE;
6355 str = js_NewString(cx, chars, length, 0);
6356 if (!str) {
6357 JS_free(cx, chars);
6358 return JS_FALSE;
6359 }
6360
6361 /* Fill the buffer, advancing chars and recycling kids as we go. */
6362 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
6363 str2 = ATOM_TO_STRING(pn2->pn_atom);
6364 length2 = str2->length;
6365 js_strncpy(chars, str2->chars, length2);
6366 chars += length2;
6367 }
6368 *chars = 0;
6369
6370 /* Atomize the result string and mutate pn to refer to it. */
6371 pn->pn_atom = js_AtomizeString(cx, str, 0);
6372 if (!pn->pn_atom)
6373 return JS_FALSE;
6374 pn->pn_type = TOK_STRING;
6375 pn->pn_op = JSOP_STRING;
6376 pn->pn_arity = PN_NULLARY;
6377 break;
6378 }
6379
6380 /* Handle a binary string concatenation. */
6381 JS_ASSERT(pn->pn_arity == PN_BINARY);
6382 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
6383 JSString *left, *right, *str;
6384
6385 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
6386 TOK_STRING)) {
6387 return JS_FALSE;
6388 }
6389 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
6390 return JS_TRUE;
6391 left = ATOM_TO_STRING(pn1->pn_atom);
6392 right = ATOM_TO_STRING(pn2->pn_atom);
6393 str = js_ConcatStrings(cx, left, right);
6394 if (!str)
6395 return JS_FALSE;
6396 pn->pn_atom = js_AtomizeString(cx, str, 0);
6397 if (!pn->pn_atom)
6398 return JS_FALSE;
6399 pn->pn_type = TOK_STRING;
6400 pn->pn_op = JSOP_STRING;
6401 pn->pn_arity = PN_NULLARY;
6402 RecycleTree(pn1, tc);
6403 RecycleTree(pn2, tc);
6404 break;
6405 }
6406
6407 /* Can't concatenate string literals, let's try numbers. */
6408 goto do_binary_op;
6409
6410 case TOK_STAR:
6411 /* The * in 'import *;' parses as a nullary star node. */
6412 if (pn->pn_arity == PN_NULLARY)
6413 break;
6414 /* FALL THROUGH */
6415
6416 case TOK_SHOP:
6417 case TOK_MINUS:
6418 case TOK_DIVOP:
6419 do_binary_op:
6420 if (pn->pn_arity == PN_LIST) {
6421 JS_ASSERT(pn->pn_count > 2);
6422 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6423 if (!FoldType(cx, pn2, TOK_NUMBER))
6424 return JS_FALSE;
6425 }
6426 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
6427 /* XXX fold only if all operands convert to number */
6428 if (pn2->pn_type != TOK_NUMBER)
6429 break;
6430 }
6431 if (!pn2) {
6432 JSOp op = pn->pn_op;
6433
6434 pn2 = pn1->pn_next;
6435 pn3 = pn2->pn_next;
6436 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
6437 return JS_FALSE;
6438 while ((pn2 = pn3) != NULL) {
6439 pn3 = pn2->pn_next;
6440 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
6441 return JS_FALSE;
6442 }
6443 }
6444 } else {
6445 JS_ASSERT(pn->pn_arity == PN_BINARY);
6446 if (!FoldType(cx, pn1, TOK_NUMBER) ||
6447 !FoldType(cx, pn2, TOK_NUMBER)) {
6448 return JS_FALSE;
6449 }
6450 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
6451 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
6452 return JS_FALSE;
6453 }
6454 }
6455 break;
6456
6457 case TOK_UNARYOP:
6458 while (pn1->pn_type == TOK_RP)
6459 pn1 = pn1->pn_kid;
6460 if (pn1->pn_type == TOK_NUMBER) {
6461 jsdouble d;
6462 int32 i;
6463
6464 /* Operate on one numeric constant. */
6465 d = pn1->pn_dval;
6466 switch (pn->pn_op) {
6467 case JSOP_BITNOT:
6468 if (!js_DoubleToECMAInt32(cx, d, &i))
6469 return JS_FALSE;
6470 d = ~i;
6471 break;
6472
6473 case JSOP_NEG:
6474 #ifdef HPUX
6475 /*
6476 * Negation of a zero doesn't produce a negative
6477 * zero on HPUX. Perform the operation by bit
6478 * twiddling.
6479 */
6480 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
6481 #else
6482 d = -d;
6483 #endif
6484 break;
6485
6486 case JSOP_POS:
6487 break;
6488
6489 case JSOP_NOT:
6490 pn->pn_type = TOK_PRIMARY;
6491 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
6492 pn->pn_arity = PN_NULLARY;
6493 /* FALL THROUGH */
6494
6495 default:
6496 /* Return early to dodge the common TOK_NUMBER code. */
6497 return JS_TRUE;
6498 }
6499 pn->pn_type = TOK_NUMBER;
6500 pn->pn_op = JSOP_NUMBER;
6501 pn->pn_arity = PN_NULLARY;
6502 pn->pn_dval = d;
6503 RecycleTree(pn1, tc);
6504 }
6505 break;
6506
6507 #if JS_HAS_XML_SUPPORT
6508 case TOK_XMLELEM:
6509 case TOK_XMLLIST:
6510 case TOK_XMLPTAGC:
6511 case TOK_XMLSTAGO:
6512 case TOK_XMLETAGO:
6513 case TOK_XMLNAME:
6514 if (pn->pn_arity == PN_LIST) {
6515 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
6516 if (!FoldXMLConstants(cx, pn, tc))
6517 return JS_FALSE;
6518 }
6519 break;
6520
6521 case TOK_AT:
6522 if (pn1->pn_type == TOK_XMLNAME) {
6523 jsval v;
6524 JSAtom *atom;
6525
6526 v = ATOM_KEY(pn1->pn_atom);
6527 if (!js_ToAttributeName(cx, &v))
6528 return JS_FALSE;
6529 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
6530 atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0);
6531 if (!atom)
6532 return JS_FALSE;
6533
6534 pn->pn_type = TOK_XMLNAME;
6535 pn->pn_op = JSOP_OBJECT;
6536 pn->pn_arity = PN_NULLARY;
6537 pn->pn_atom = atom;
6538 RecycleTree(pn1, tc);
6539 }
6540 break;
6541 #endif /* JS_HAS_XML_SUPPORT */
6542
6543 default:;
6544 }
6545
6546 return JS_TRUE;
6547 }
6548