1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 /*
41 * JS parser.
42 *
43 * This is a recursive-descent parser for the JavaScript language specified by
44 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
45 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
46 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
47 * After tree construction, it rewrites trees to fold constants and evaluate
48 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
49 * generate bytecode.
50 *
51 * This parser attempts no error recovery.
52 */
53 #include "jsstddef.h"
54 #include <stdlib.h>
55 #include <string.h>
56 #include <math.h>
57 #include "jstypes.h"
58 #include "jsarena.h" /* Added by JSIFY */
59 #include "jsutil.h" /* Added by JSIFY */
60 #include "jsapi.h"
61 #include "jsatom.h"
62 #include "jscntxt.h"
63 #include "jsconfig.h"
64 #include "jsemit.h"
65 #include "jsfun.h"
66 #include "jsinterp.h"
67 #include "jslock.h"
68 #include "jsnum.h"
69 #include "jsobj.h"
70 #include "jsopcode.h"
71 #include "jsparse.h"
72 #include "jsscan.h"
73 #include "jsscope.h"
74 #include "jsscript.h"
75 #include "jsstr.h"
76
77 #if JS_HAS_XML_SUPPORT
78 #include "jsxml.h"
79 #endif
80
81 /*
82 * JS parsers, from lowest to highest precedence.
83 *
84 * Each parser takes a context, a token stream, and a tree context struct.
85 * Each returns a parse node tree or null on error.
86 */
87
88 typedef JSParseNode *
89 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
90
91 typedef JSParseNode *
92 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
93 JSBool allowCallSyntax);
94
95 static JSParser FunctionStmt;
96 #if JS_HAS_LEXICAL_CLOSURE
97 static JSParser FunctionExpr;
98 #endif
99 static JSParser Statements;
100 static JSParser Statement;
101 static JSParser Variables;
102 static JSParser Expr;
103 static JSParser AssignExpr;
104 static JSParser CondExpr;
105 static JSParser OrExpr;
106 static JSParser AndExpr;
107 static JSParser BitOrExpr;
108 static JSParser BitXorExpr;
109 static JSParser BitAndExpr;
110 static JSParser EqExpr;
111 static JSParser RelExpr;
112 static JSParser ShiftExpr;
113 static JSParser AddExpr;
114 static JSParser MulExpr;
115 static JSParser UnaryExpr;
116 static JSMemberParser MemberExpr;
117 static JSParser PrimaryExpr;
118
119 /*
120 * Insist that the next token be of type tt, or report errno and return null.
121 * NB: this macro uses cx and ts from its lexical environment.
122 */
123 #define MUST_MATCH_TOKEN(tt, errno) \
124 JS_BEGIN_MACRO \
125 if (js_GetToken(cx, ts) != tt) { \
126 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
127 errno); \
128 return NULL; \
129 } \
130 JS_END_MACRO
131
132 #define CHECK_RECURSION() \
133 JS_BEGIN_MACRO \
134 int stackDummy; \
135 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \
136 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \
137 JSMSG_OVER_RECURSED); \
138 return NULL; \
139 } \
140 JS_END_MACRO
141
142 #ifdef METER_PARSENODES
143 static uint32 parsenodes = 0;
144 static uint32 maxparsenodes = 0;
145 static uint32 recyclednodes = 0;
146 #endif
147
148 static JSParseNode *
149 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
150 1597333 {
151 JSParseNode *next;
152
153 1597333 if (!pn)
154 692216 return NULL;
155 JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */
156 905117 next = pn->pn_next;
157 905117 pn->pn_next = tc->nodeList;
158 905117 tc->nodeList = pn;
159 #ifdef METER_PARSENODES
160 recyclednodes++;
161 #endif
162 905117 return next;
163 }
164
165 static JSParseNode *
166 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
167 2701722 {
168 JSParseNode *pn;
169
170 2701722 pn = tc->nodeList;
171 2701722 if (!pn) {
172 982244 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
173 982244 if (!pn)
174 0 JS_ReportOutOfMemory(cx);
175 } else {
176 1719478 tc->nodeList = pn->pn_next;
177
178 /* Recycle immediate descendents only, to save work and working set. */
179 1719478 switch (pn->pn_arity) {
180 case PN_FUNC:
181 7663 RecycleTree(pn->pn_body, tc);
182 7663 break;
183 case PN_LIST:
184 386354 if (pn->pn_head) {
185 /* XXX check for dup recycles in the list */
186 386347 *pn->pn_tail = tc->nodeList;
187 386347 tc->nodeList = pn->pn_head;
188 #ifdef METER_PARSENODES
189 recyclednodes += pn->pn_count;
190 #endif
191 }
192 386354 break;
193 case PN_TERNARY:
194 12795 RecycleTree(pn->pn_kid1, tc);
195 12795 RecycleTree(pn->pn_kid2, tc);
196 12795 RecycleTree(pn->pn_kid3, tc);
197 12795 break;
198 case PN_BINARY:
199 41941 RecycleTree(pn->pn_left, tc);
200 41941 RecycleTree(pn->pn_right, tc);
201 41941 break;
202 case PN_UNARY:
203 351095 RecycleTree(pn->pn_kid, tc);
204 351095 break;
205 case PN_NAME:
206 719169 RecycleTree(pn->pn_expr, tc);
207 break;
208 case PN_NULLARY:
209 break;
210 }
211 }
212 #ifdef METER_PARSENODES
213 if (pn) {
214 parsenodes++;
215 if (parsenodes - recyclednodes > maxparsenodes)
216 maxparsenodes = parsenodes - recyclednodes;
217 }
218 #endif
219 2701722 return pn;
220 }
221
222 /*
223 * Allocate a JSParseNode from cx's temporary arena.
224 */
225 static JSParseNode *
226 NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity,
227 JSTreeContext *tc)
228 2602726 {
229 JSParseNode *pn;
230 JSToken *tp;
231
232 2602726 pn = NewOrRecycledNode(cx, tc);
233 2602726 if (!pn)
234 0 return NULL;
235 2602726 tp = &CURRENT_TOKEN(ts);
236 2602726 pn->pn_type = tp->type;
237 2602726 pn->pn_pos = tp->pos;
238 2602726 pn->pn_op = JSOP_NOP;
239 2602726 pn->pn_arity = arity;
240 2602726 pn->pn_next = NULL;
241 #if JS_HAS_XML_SUPPORT
242 2602726 pn->pn_ts = ts;
243 #endif
244 2602726 return pn;
245 }
246
247 static JSParseNode *
248 NewBinary(JSContext *cx, JSTokenType tt,
249 JSOp op, JSParseNode *left, JSParseNode *right,
250 JSTreeContext *tc)
251 104360 {
252 JSParseNode *pn, *pn1, *pn2;
253
254 104360 if (!left || !right)
255 0 return NULL;
256
257 /*
258 * Flatten a left-associative (left-heavy) tree of a given operator into
259 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
260 */
261 104360 if (left->pn_type == tt &&
262 left->pn_op == op &&
263 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
264 5364 if (left->pn_arity != PN_LIST) {
265 4440 pn1 = left->pn_left, pn2 = left->pn_right;
266 4440 left->pn_arity = PN_LIST;
267 4440 PN_INIT_LIST_1(left, pn1);
268 4440 PN_APPEND(left, pn2);
269 4440 if (tt == TOK_PLUS) {
270 4440 if (pn1->pn_type == TOK_STRING)
271 2933 left->pn_extra |= PNX_STRCAT;
272 1507 else if (pn1->pn_type != TOK_NUMBER)
273 1507 left->pn_extra |= PNX_CANTFOLD;
274 4440 if (pn2->pn_type == TOK_STRING)
275 1486 left->pn_extra |= PNX_STRCAT;
276 2954 else if (pn2->pn_type != TOK_NUMBER)
277 2954 left->pn_extra |= PNX_CANTFOLD;
278 }
279 }
280 5364 PN_APPEND(left, right);
281 5364 left->pn_pos.end = right->pn_pos.end;
282 5364 if (tt == TOK_PLUS) {
283 5364 if (right->pn_type == TOK_STRING)
284 3488 left->pn_extra |= PNX_STRCAT;
285 1876 else if (right->pn_type != TOK_NUMBER)
286 1876 left->pn_extra |= PNX_CANTFOLD;
287 }
288 5364 return left;
289 }
290
291 /*
292 * Fold constant addition immediately, to conserve node space and, what's
293 * more, so js_FoldConstants never sees mixed addition and concatenation
294 * operations with more than one leading non-string operand in a PN_LIST
295 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
296 * to "3pt", not "12pt").
297 */
298 98996 if (tt == TOK_PLUS &&
299 left->pn_type == TOK_NUMBER &&
300 right->pn_type == TOK_NUMBER) {
301 0 left->pn_dval += right->pn_dval;
302 0 left->pn_pos.end = right->pn_pos.end;
303 0 RecycleTree(right, tc);
304 0 return left;
305 }
306
307 98996 pn = NewOrRecycledNode(cx, tc);
308 98996 if (!pn)
309 0 return NULL;
310 98996 pn->pn_type = tt;
311 98996 pn->pn_pos.begin = left->pn_pos.begin;
312 98996 pn->pn_pos.end = right->pn_pos.end;
313 98996 pn->pn_op = op;
314 98996 pn->pn_arity = PN_BINARY;
315 98996 pn->pn_left = left;
316 98996 pn->pn_right = right;
317 98996 pn->pn_next = NULL;
318 #if JS_HAS_XML_SUPPORT
319 98996 pn->pn_ts = NULL;
320 #endif
321 98996 return pn;
322 }
323
324 #if JS_HAS_GETTER_SETTER
325 static JSTokenType
326 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
327 1486660 {
328 JSAtom *atom;
329 JSRuntime *rt;
330 JSOp op;
331 const char *name;
332
333 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
334 1486660 atom = CURRENT_TOKEN(ts).t_atom;
335 1486660 rt = cx->runtime;
336 1486660 if (atom == rt->atomState.getterAtom)
337 0 op = JSOP_GETTER;
338 1486660 else if (atom == rt->atomState.setterAtom)
339 0 op = JSOP_SETTER;
340 else
341 1486660 return TOK_NAME;
342 0 if (js_PeekTokenSameLine(cx, ts) != tt)
343 0 return TOK_NAME;
344 0 (void) js_GetToken(cx, ts);
345 0 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
346 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
347 JSMSG_BAD_GETTER_OR_SETTER,
348 (op == JSOP_GETTER)
349 ? js_getter_str
350 : js_setter_str);
351 0 return TOK_ERROR;
352 }
353 0 CURRENT_TOKEN(ts).t_op = op;
354 0 name = js_AtomToPrintableString(cx, atom);
355 0 if (!name ||
356 !js_ReportCompileErrorNumber(cx, ts,
357 JSREPORT_TS |
358 JSREPORT_WARNING |
359 JSREPORT_STRICT,
360 JSMSG_DEPRECATED_USAGE,
361 name)) {
362 0 return TOK_ERROR;
363 }
364 0 return tt;
365 }
366 #endif
367
368 /*
369 * Parse a top-level JS script.
370 */
371 JS_FRIEND_API(JSParseNode *)
372 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
373 0 {
374 JSStackFrame *fp, frame;
375 JSTreeContext tc;
376 JSParseNode *pn;
377
378 /*
379 * Push a compiler frame if we have no frames, or if the top frame is a
380 * lightweight function activation, or if its scope chain doesn't match
381 * the one passed to us.
382 */
383 0 fp = cx->fp;
384 0 if (!fp || !fp->varobj || fp->scopeChain != chain) {
385 0 memset(&frame, 0, sizeof frame);
386 0 frame.varobj = frame.scopeChain = chain;
387 0 if (cx->options & JSOPTION_VAROBJFIX) {
388 0 while ((chain = JS_GetParent(cx, chain)) != NULL)
389 0 frame.varobj = chain;
390 }
391 0 frame.down = fp;
392 0 if (fp) {
393 0 frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
394 JSFRAME_SCRIPT_OBJECT);
395 }
396 0 cx->fp = &frame;
397 }
398
399 /*
400 * Protect atoms from being collected by a GC activation, which might
401 * - nest on this thread due to out of memory (the so-called "last ditch"
402 * GC attempted within js_NewGCThing), or
403 * - run for any reason on another thread if this thread is suspended on
404 * an object lock before it finishes generating bytecode into a script
405 * protected from the GC by a root or a stack frame reference.
406 */
407 0 JS_KEEP_ATOMS(cx->runtime);
408 0 TREE_CONTEXT_INIT(&tc);
409 0 pn = Statements(cx, ts, &tc);
410 0 if (pn) {
411 0 if (!js_MatchToken(cx, ts, TOK_EOF)) {
412 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
413 JSMSG_SYNTAX_ERROR);
414 0 pn = NULL;
415 } else {
416 0 pn->pn_type = TOK_LC;
417 0 if (!js_FoldConstants(cx, pn, &tc))
418 0 pn = NULL;
419 }
420 }
421
422 TREE_CONTEXT_FINISH(&tc);
423 0 JS_UNKEEP_ATOMS(cx->runtime);
424 0 cx->fp = fp;
425 0 return pn;
426 }
427
428 /*
429 * Compile a top-level script.
430 */
431 JS_FRIEND_API(JSBool)
432 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
433 JSCodeGenerator *cg)
434 2201 {
435 JSStackFrame *fp, frame;
436 uint32 flags;
437 JSParseNode *pn;
438 JSBool ok;
439 #ifdef METER_PARSENODES
440 void *sbrk(ptrdiff_t), *before = sbrk(0);
441 #endif
442
443 /*
444 * Push a compiler frame if we have no frames, or if the top frame is a
445 * lightweight function activation, or if its scope chain doesn't match
446 * the one passed to us.
447 */
448 2201 fp = cx->fp;
449 2201 if (!fp || !fp->varobj || fp->scopeChain != chain) {
450 34 memset(&frame, 0, sizeof frame);
451 34 frame.varobj = frame.scopeChain = chain;
452 34 if (cx->options & JSOPTION_VAROBJFIX) {
453 0 while ((chain = JS_GetParent(cx, chain)) != NULL)
454 0 frame.varobj = chain;
455 }
456 34 frame.down = fp;
457 34 if (fp) {
458 34 frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
459 JSFRAME_SCRIPT_OBJECT);
460 }
461 34 cx->fp = &frame;
462 }
463 2201 flags = cx->fp->flags;
464 2201 cx->fp->flags = flags |
465 (JS_HAS_COMPILE_N_GO_OPTION(cx)
466 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
467 : JSFRAME_COMPILING);
468
469 /* Prevent GC activation while compiling. */
470 2201 JS_KEEP_ATOMS(cx->runtime);
471
472 2201 pn = Statements(cx, ts, &cg->treeContext);
473 2201 if (!pn) {
474 0 ok = JS_FALSE;
475 2201 } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
476 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
477 JSMSG_SYNTAX_ERROR);
478 0 ok = JS_FALSE;
479 } else {
480 #ifdef METER_PARSENODES
481 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
482 (char *)sbrk(0) - (char *)before,
483 parsenodes,
484 maxparsenodes,
485 parsenodes - recyclednodes);
486 before = sbrk(0);
487 #endif
488
489 /*
490 * No need to emit code here -- Statements already has, for each
491 * statement in turn. Search for TCF_COMPILING in Statements, below.
492 * That flag is set for every tc == &cg->treeContext, and it implies
493 * that the tc can be downcast to a cg and used to emit code during
494 * parsing, rather than at the end of the parse phase.
495 */
496 JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
497 2201 ok = JS_TRUE;
498 }
499
500 #ifdef METER_PARSENODES
501 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
502 (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
503 #endif
504 #ifdef JS_ARENAMETER
505 JS_DumpArenaStats(stdout);
506 #endif
507 2201 JS_UNKEEP_ATOMS(cx->runtime);
508 2201 cx->fp->flags = flags;
509 2201 cx->fp = fp;
510 2201 return ok;
511 }
512
513 /*
514 * Insist on a final return before control flows out of pn, but don't be too
515 * smart about loops (do {...; return e2;} while(0) at the end of a function
516 * that contains an early return e1 will get a strict-option-only warning).
517 */
518 #define ENDS_IN_OTHER 0
519 #define ENDS_IN_RETURN 1
520 #define ENDS_IN_BREAK 2
521
522 static int
523 HasFinalReturn(JSParseNode *pn)
524 0 {
525 uintN rv, rv2, hasDefault;
526 JSParseNode *pn2, *pn3;
527
528 0 switch (pn->pn_type) {
529 case TOK_LC:
530 0 if (!pn->pn_head)
531 0 return ENDS_IN_OTHER;
532 0 return HasFinalReturn(PN_LAST(pn));
533
534 case TOK_IF:
535 0 rv = HasFinalReturn(pn->pn_kid2);
536 0 if (pn->pn_kid3)
537 0 rv &= HasFinalReturn(pn->pn_kid3);
538 0 return rv;
539
540 #if JS_HAS_SWITCH_STATEMENT
541 case TOK_SWITCH:
542 0 rv = ENDS_IN_RETURN;
543 0 hasDefault = ENDS_IN_OTHER;
544 0 for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
545 0 if (pn2->pn_type == TOK_DEFAULT)
546 0 hasDefault = ENDS_IN_RETURN;
547 0 pn3 = pn2->pn_right;
548 JS_ASSERT(pn3->pn_type == TOK_LC);
549 0 if (pn3->pn_head) {
550 0 rv2 = HasFinalReturn(PN_LAST(pn3));
551 0 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
552 /* Falling through to next case or default. */;
553 else
554 0 rv &= rv2;
555 }
556 }
557 /* If a final switch has no default case, we judge it harshly. */
558 0 rv &= hasDefault;
559 0 return rv;
560 #endif /* JS_HAS_SWITCH_STATEMENT */
561
562 case TOK_BREAK:
563 0 return ENDS_IN_BREAK;
564
565 case TOK_WITH:
566 0 return HasFinalReturn(pn->pn_right);
567
568 case TOK_RETURN:
569 0 return ENDS_IN_RETURN;
570
571 case TOK_COLON:
572 0 return HasFinalReturn(pn->pn_expr);
573
574 #if JS_HAS_EXCEPTIONS
575 case TOK_THROW:
576 0 return ENDS_IN_RETURN;
577
578 case TOK_TRY:
579 /* If we have a finally block that returns, we are done. */
580 0 if (pn->pn_kid3) {
581 0 rv = HasFinalReturn(pn->pn_kid3);
582 0 if (rv == ENDS_IN_RETURN)
583 0 return rv;
584 }
585
586 /* Else check the try block and any and all catch statements. */
587 0 rv = HasFinalReturn(pn->pn_kid1);
588 0 if (pn->pn_kid2)
589 0 rv &= HasFinalReturn(pn->pn_kid2);
590 0 return rv;
591
592 case TOK_CATCH:
593 /* Check this block's code and iterate over further catch blocks. */
594 0 rv = HasFinalReturn(pn->pn_kid3);
595 0 for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
596 0 rv &= HasFinalReturn(pn2->pn_kid3);
597 0 return rv;
598 #endif
599
600 default:
601 0 return ENDS_IN_OTHER;
602 }
603 }
604
605 static JSBool
606 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
607 0 {
608 JSFunction *fun;
609 JSBool ok;
610
611 0 fun = cx->fp->fun;
612 0 if (fun->atom) {
613 0 char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
614 0 ok = js_ReportCompileErrorNumber(cx, ts,
615 JSREPORT_TS |
616 JSREPORT_WARNING |
617 JSREPORT_STRICT,
618 JSMSG_NO_RETURN_VALUE, name);
619 } else {
620 0 ok = js_ReportCompileErrorNumber(cx, ts,
621 JSREPORT_TS |
622 JSREPORT_WARNING |
623 JSREPORT_STRICT,
624 JSMSG_ANON_NO_RETURN_VALUE);
625 }
626 0 return ok;
627 }
628
629 static JSBool
630 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
631 0 {
632 0 return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
633 }
634
635 static JSParseNode *
636 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
637 JSTreeContext *tc)
638 7713 {
639 JSStackFrame *fp, frame;
640 JSObject *funobj;
641 uintN oldflags;
642 JSParseNode *pn;
643
644 7713 fp = cx->fp;
645 7713 funobj = fun->object;
646 7713 if (!fp || fp->fun != fun || fp->varobj != funobj ||
647 fp->scopeChain != funobj) {
648 7713 memset(&frame, 0, sizeof frame);
649 7713 frame.fun = fun;
650 7713 frame.varobj = frame.scopeChain = funobj;
651 7713 frame.down = fp;
652 7713 if (fp)
653 7713 frame.flags = fp->flags & JSFRAME_COMPILE_N_GO;
654 7713 cx->fp = &frame;
655 }
656
657 7713 oldflags = tc->flags;
658 7713 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
659 7713 tc->flags |= TCF_IN_FUNCTION;
660 7713 pn = Statements(cx, ts, tc);
661
662 /* Check for falling off the end of a function that returns a value. */
663 7713 if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
664 0 if (!CheckFinalReturn(cx, ts, pn))
665 0 pn = NULL;
666 }
667
668 7713 cx->fp = fp;
669 7713 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
670 7713 return pn;
671 }
672
673 /*
674 * Compile a JS function body, which might appear as the value of an event
675 * handler attribute in an HTML <INPUT> tag.
676 */
677 JSBool
678 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
679 0 {
680 JSArenaPool codePool, notePool;
681 JSCodeGenerator funcg;
682 JSStackFrame *fp, frame;
683 JSObject *funobj;
684 JSParseNode *pn;
685 JSBool ok;
686
687 0 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
688 0 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
689 0 if (!js_InitCodeGenerator(cx, &funcg, &codePool, &notePool,
690 ts->filename, ts->lineno,
691 ts->principals)) {
692 0 return JS_FALSE;
693 }
694
695 /* Prevent GC activation while compiling. */
696 0 JS_KEEP_ATOMS(cx->runtime);
697
698 /* Push a JSStackFrame for use by FunctionBody. */
699 0 fp = cx->fp;
700 0 funobj = fun->object;
701 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
702 fp->scopeChain != funobj));
703 0 memset(&frame, 0, sizeof frame);
704 0 frame.fun = fun;
705 0 frame.varobj = frame.scopeChain = funobj;
706 0 frame.down = fp;
707 0 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
708 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
709 : JSFRAME_COMPILING;
710 0 cx->fp = &frame;
711
712 /* Ensure that the body looks like a block statement to js_EmitTree. */
713 0 CURRENT_TOKEN(ts).type = TOK_LC;
714 0 pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
715 0 if (!pn) {
716 0 ok = JS_FALSE;
717 } else {
718 /*
719 * No need to emit code here -- Statements (via FunctionBody) already
720 * has. See similar comment in js_CompileTokenStream, and bug 108257.
721 */
722 0 fun->u.script = js_NewScriptFromCG(cx, &funcg, fun);
723 0 if (!fun->u.script) {
724 0 ok = JS_FALSE;
725 } else {
726 0 fun->interpreted = JS_TRUE;
727 0 if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
728 0 fun->flags |= JSFUN_HEAVYWEIGHT;
729 0 ok = JS_TRUE;
730 }
731 }
732
733 /* Restore saved state and release code generation arenas. */
734 0 cx->fp = fp;
735 0 JS_UNKEEP_ATOMS(cx->runtime);
736 0 js_FinishCodeGenerator(cx, &funcg);
737 0 JS_FinishArenaPool(&codePool);
738 0 JS_FinishArenaPool(&notePool);
739 0 return ok;
740 }
741
742 static JSParseNode *
743 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
744 JSBool lambda)
745 7713 {
746 JSOp op, prevop;
747 JSParseNode *pn, *body, *result;
748 JSAtom *funAtom, *objAtom, *argAtom;
749 JSStackFrame *fp;
750 JSObject *varobj, *pobj;
751 JSAtomListElement *ale;
752 JSProperty *prop;
753 JSFunction *fun;
754 uintN dupflag;
755 JSBool ok;
756 JSTreeContext funtc;
757
758 /* Make a TOK_FUNCTION node. */
759 #if JS_HAS_GETTER_SETTER
760 7713 op = CURRENT_TOKEN(ts).t_op;
761 #endif
762 7713 pn = NewParseNode(cx, ts, PN_FUNC, tc);
763 7713 if (!pn)
764 0 return NULL;
765
766 /* Scan the optional function name into funAtom. */
767 7713 funAtom = js_MatchToken(cx, ts, TOK_NAME) ? CURRENT_TOKEN(ts).t_atom : NULL;
768 #if !JS_HAS_LEXICAL_CLOSURE
769 if (!funAtom && !lambda) {
770 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
771 JSMSG_SYNTAX_ERROR);
772 return NULL;
773 }
774 #endif
775
776 /* Find the nearest variable-declaring scope and use it as our parent. */
777 7713 fp = cx->fp;
778 7713 varobj = fp->varobj;
779
780 /*
781 * Record names for function statements in tc->decls so we know when to
782 * avoid optimizing variable references that might name a function.
783 */
784 7713 if (!lambda && funAtom) {
785 7659 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
786 7659 if (ale) {
787 132 prevop = ALE_JSOP(ale);
788 132 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
789 0 const char *name = js_AtomToPrintableString(cx, funAtom);
790 0 if (!name ||
791 !js_ReportCompileErrorNumber(cx, ts,
792 (prevop != JSOP_DEFCONST)
793 ? JSREPORT_TS |
794 JSREPORT_WARNING |
795 JSREPORT_STRICT
796 : JSREPORT_TS | JSREPORT_ERROR,
797 JSMSG_REDECLARED_VAR,
798 (prevop == JSOP_DEFFUN ||
799 prevop == JSOP_CLOSURE)
800 ? js_function_str
801 : (prevop == JSOP_DEFCONST)
802 ? js_const_str
803 : js_var_str,
804 name)) {
805 0 return NULL;
806 }
807 }
808 132 if (tc->topStmt && prevop == JSOP_DEFVAR)
809 0 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
810 } else {
811 7527 ale = js_IndexAtom(cx, funAtom, &tc->decls);
812 7527 if (!ale)
813 0 return NULL;
814 }
815 7659 ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
816
817 #if JS_HAS_LEXICAL_CLOSURE
818 /*
819 * A function nested at top level inside another's body needs only a
820 * local variable to bind its name to its value, and not an activation
821 * object property (it might also need the activation property, if the
822 * outer function contains with statements, e.g., but the stack slot
823 * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
824 * JSOP_GETVAR bytecode).
825 */
826 7659 if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
827 JSScopeProperty *sprop;
828
829 /*
830 * Define a property on the outer function so that BindNameToSlot
831 * can properly optimize accesses.
832 */
833 JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
834 JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
835 0 if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
836 &pobj, &prop)) {
837 0 return NULL;
838 }
839 0 if (prop)
840 0 OBJ_DROP_PROPERTY(cx, pobj, prop);
841 0 sprop = NULL;
842 0 if (!prop ||
843 pobj != varobj ||
844 (sprop = (JSScopeProperty *)prop,
845 sprop->getter != js_GetLocalVariable)) {
846 uintN sflags;
847
848 /*
849 * Use SPROP_IS_DUPLICATE if there is a formal argument of the
850 * same name, so the decompiler can find the parameter name.
851 */
852 0 sflags = (sprop && sprop->getter == js_GetArgument)
853 ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID
854 : SPROP_HAS_SHORTID;
855 0 if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
856 js_GetLocalVariable,
857 js_SetLocalVariable,
858 SPROP_INVALID_SLOT,
859 JSPROP_PERMANENT | JSPROP_SHARED,
860 sflags, fp->fun->nvars)) {
861 0 return NULL;
862 }
863 0 if (fp->fun->nvars == JS_BITMASK(16)) {
864 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
865 JSMSG_TOO_MANY_FUN_VARS);
866 0 return NULL;
867 }
868 0 fp->fun->nvars++;
869 }
870 }
871 #endif
872 }
873
874 7713 fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
875 funAtom);
876 7713 if (!fun)
877 0 return NULL;
878 #if JS_HAS_GETTER_SETTER
879 7713 if (op != JSOP_NOP)
880 0 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
881 #endif
882
883
884 /*
885 * Set interpreted early so js_EmitTree can test it to decide whether to
886 * eliminate useless expressions.
887 */
888 7713 fun->interpreted = JS_TRUE;
889
890 /*
891 * Atomize fun->object early to protect against a last-ditch GC under
892 * js_LookupHiddenProperty.
893 *
894 * Absent use of the new scoped local GC roots API around compiler calls,
895 * we need to atomize here to protect against a GC activation. Atoms are
896 * protected from GC during compilation by the JS_FRIEND_API entry points
897 * in this file. There doesn't seem to be any gain in switching from the
898 * atom-keeping method to the bulkier, slower scoped local roots method.
899 */
900 7713 objAtom = js_AtomizeObject(cx, fun->object, 0);
901 7713 if (!objAtom)
902 0 return NULL;
903
904 /* Now parse formal argument list and compute fun->nargs. */
905 7713 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
906 7713 if (!js_MatchToken(cx, ts, TOK_RP)) {
907 do {
908 36647 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
909 36647 argAtom = CURRENT_TOKEN(ts).t_atom;
910 36647 pobj = NULL;
911 36647 if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom),
912 &pobj, &prop)) {
913 0 return NULL;
914 }
915 36647 dupflag = 0;
916 36647 if (prop) {
917 66 ok = JS_TRUE;
918 66 if (pobj == fun->object &&
919 ((JSScopeProperty *) prop)->getter == js_GetArgument) {
920 66 const char *name = js_AtomToPrintableString(cx, argAtom);
921
922 /*
923 * A duplicate parameter name. We force a duplicate node
924 * on the SCOPE_LAST_PROP(scope) list with the same id,
925 * distinguished by the SPROP_IS_DUPLICATE flag, and not
926 * mapped by an entry in scope.
927 */
928 66 ok = name &&
929 js_ReportCompileErrorNumber(cx, ts,
930 JSREPORT_TS |
931 JSREPORT_WARNING |
932 JSREPORT_STRICT,
933 JSMSG_DUPLICATE_FORMAL,
934 name);
935
936 66 dupflag = SPROP_IS_DUPLICATE;
937 }
938 66 OBJ_DROP_PROPERTY(cx, pobj, prop);
939 66 if (!ok)
940 0 return NULL;
941 66 prop = NULL;
942 }
943 36647 if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom),
944 js_GetArgument, js_SetArgument,
945 SPROP_INVALID_SLOT,
946 JSPROP_PERMANENT | JSPROP_SHARED,
947 dupflag | SPROP_HAS_SHORTID,
948 fun->nargs)) {
949 0 return NULL;
950 }
951 36647 if (fun->nargs == JS_BITMASK(16)) {
952 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
953 JSMSG_TOO_MANY_FUN_ARGS);
954 0 return NULL;
955 }
956 36647 fun->nargs++;
957 36647 } while (js_MatchToken(cx, ts, TOK_COMMA));
958
959 7593 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
960 }
961
962 7713 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
963 7713 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
964
965 7713 TREE_CONTEXT_INIT(&funtc);
966 7713 body = FunctionBody(cx, ts, fun, &funtc);
967 7713 if (!body)
968 0 return NULL;
969
970 7713 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
971 7713 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
972
973 #if JS_HAS_LEXICAL_CLOSURE
974 /*
975 * If we collected flags that indicate nested heavyweight functions, or
976 * this function contains heavyweight-making statements (references to
977 * __parent__ or __proto__; use of with, eval, import, or export; and
978 * assignment to arguments), flag the function as heavyweight (requiring
979 * a call object per invocation).
980 */
981 7713 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
982 0 fun->flags |= JSFUN_HEAVYWEIGHT;
983 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
984 } else {
985 /*
986 * If this function is a named statement function not at top-level
987 * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
988 * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
989 * enclosing function, if any, must be heavyweight.
990 */
991 7713 if ((!lambda && funAtom && tc->topStmt) ||
992 (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
993 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
994 }
995 }
996 #endif
997
998 7713 result = pn;
999 #if JS_HAS_LEXICAL_CLOSURE
1000 7713 if (lambda) {
1001 /*
1002 * ECMA ed. 3 standard: function expression, possibly anonymous.
1003 */
1004 54 op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1005 7659 } else if (!funAtom) {
1006 /*
1007 * If this anonymous function definition is *not* embedded within a
1008 * larger expression, we treat it as an expression statement, not as
1009 * a function declaration -- and not as a syntax error (as ECMA-262
1010 * Edition 3 would have it). Backward compatibility trumps all.
1011 */
1012 0 result = NewParseNode(cx, ts, PN_UNARY, tc);
1013 0 if (!result)
1014 0 return NULL;
1015 0 result->pn_type = TOK_SEMI;
1016 0 result->pn_pos = pn->pn_pos;
1017 0 result->pn_kid = pn;
1018 0 op = JSOP_ANONFUNOBJ;
1019 7659 } else if (tc->topStmt) {
1020 /*
1021 * ECMA ed. 3 extension: a function expression statement not at the
1022 * top level, e.g., in a compound statement such as the "then" part
1023 * of an "if" statement, binds a closure only if control reaches that
1024 * sub-statement.
1025 */
1026 0 op = JSOP_CLOSURE;
1027 } else
1028 #endif
1029 7659 op = JSOP_NOP;
1030
1031 7713 pn->pn_funAtom = objAtom;
1032 7713 pn->pn_op = op;
1033 7713 pn->pn_body = body;
1034 7713 pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
1035 7713 pn->pn_tryCount = funtc.tryCount;
1036 TREE_CONTEXT_FINISH(&funtc);
1037 7713 return result;
1038 }
1039
1040 static JSParseNode *
1041 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1042 7659 {
1043 7659 return FunctionDef(cx, ts, tc, JS_FALSE);
1044 }
1045
1046 #if JS_HAS_LEXICAL_CLOSURE
1047 static JSParseNode *
1048 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1049 54 {
1050 54 return FunctionDef(cx, ts, tc, JS_TRUE);
1051 }
1052 #endif
1053
1054 /*
1055 * Parse the statements in a block, creating a TOK_LC node that lists the
1056 * statements' trees. If called from block-parsing code, the caller must
1057 * match { before and } after.
1058 */
1059 static JSParseNode *
1060 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1061 56692 {
1062 JSParseNode *pn, *pn2;
1063 JSTokenType tt;
1064
1065 56692 CHECK_RECURSION();
1066
1067 56692 pn = NewParseNode(cx, ts, PN_LIST, tc);
1068 56692 if (!pn)
1069 0 return NULL;
1070 56692 PN_INIT_LIST(pn);
1071
1072 56692 ts->flags |= TSF_OPERAND;
1073 664390 while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
1074 551006 ts->flags &= ~TSF_OPERAND;
1075 551006 pn2 = Statement(cx, ts, tc);
1076 551006 if (!pn2) {
1077 0 if (ts->flags & TSF_EOF)
1078 0 ts->flags |= TSF_UNEXPECTED_EOF;
1079 0 return NULL;
1080 }
1081 551006 ts->flags |= TSF_OPERAND;
1082
1083 /* If compiling top-level statements, emit as we go to save space. */
1084 855285 if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1085 304279 if (cx->fp->fun &&
1086 JS_HAS_STRICT_OPTION(cx) &&
1087 (tc->flags & TCF_RETURN_EXPR)) {
1088 /*
1089 * Check pn2 for lack of a final return statement if it is the
1090 * last statement in the block.
1091 */
1092 0 tt = js_PeekToken(cx, ts);
1093 0 if ((tt == TOK_EOF || tt == TOK_RC) &&
1094 !CheckFinalReturn(cx, ts, pn2)) {
1095 0 tt = TOK_ERROR;
1096 0 break;
1097 }
1098
1099 /*
1100 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1101 * CheckFinalReturn again.
1102 */
1103 0 tc->flags &= ~TCF_RETURN_EXPR;
1104 }
1105 304279 if (!js_FoldConstants(cx, pn2, tc) ||
1106 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1107 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1108 0 tt = TOK_ERROR;
1109 0 break;
1110 }
1111 304279 RecycleTree(pn2, tc);
1112 } else {
1113 246727 PN_APPEND(pn, pn2);
1114 }
1115 }
1116 56692 ts->flags &= ~TSF_OPERAND;
1117 56692 if (tt == TOK_ERROR)
1118 0 return NULL;
1119
1120 56692 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1121 56692 return pn;
1122 }
1123
1124 static JSParseNode *
1125 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1126 34937 {
1127 JSParseNode *pn, *pn2;
1128
1129 34937 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1130 34937 pn = Expr(cx, ts, tc);
1131 34937 if (!pn)
1132 0 return NULL;
1133 34937 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1134
1135 /*
1136 * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1137 * greater precedence than ==.
1138 * XXX not ECMA, but documented in several books -- now a strict warning.
1139 */
1140 34937 if (pn->pn_type == TOK_ASSIGN &&
1141 pn->pn_op == JSOP_NOP &&
1142 pn->pn_right->pn_type > TOK_EQOP)
1143 {
1144 0 JSBool rewrite = !JS_VERSION_IS_ECMA(cx);
1145 0 if (!js_ReportCompileErrorNumber(cx, ts,
1146 JSREPORT_TS |
1147 JSREPORT_WARNING |
1148 JSREPORT_STRICT,
1149 JSMSG_EQUAL_AS_ASSIGN,
1150 rewrite
1151 ? "\nAssuming equality test"
1152 : "")) {
1153 0 return NULL;
1154 }
1155 0 if (rewrite) {
1156 0 pn->pn_type = TOK_EQOP;
1157 0 pn->pn_op = (JSOp)cx->jsop_eq;
1158 0 pn2 = pn->pn_left;
1159 0 switch (pn2->pn_op) {
1160 case JSOP_SETNAME:
1161 0 pn2->pn_op = JSOP_NAME;
1162 0 break;
1163 case JSOP_SETPROP:
1164 0 pn2->pn_op = JSOP_GETPROP;
1165 0 break;
1166 case JSOP_SETELEM:
1167 0 pn2->pn_op = JSOP_GETELEM;
1168 break;
1169 default:
1170 JS_ASSERT(0);
1171 }
1172 }
1173 }
1174 34937 return pn;
1175 }
1176
1177 static JSBool
1178 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1179 1188 {
1180 JSAtom *label;
1181 #if JS_HAS_LABEL_STATEMENT
1182 JSTokenType tt;
1183
1184 1188 tt = js_PeekTokenSameLine(cx, ts);
1185 1188 if (tt == TOK_ERROR)
1186 0 return JS_FALSE;
1187 1188 if (tt == TOK_NAME) {
1188 0 (void) js_GetToken(cx, ts);
1189 0 label = CURRENT_TOKEN(ts).t_atom;
1190 } else {
1191 1188 label = NULL;
1192 }
1193 #else
1194 label = NULL;
1195 #endif
1196 1188 pn->pn_atom = label;
1197 1188 return JS_TRUE;
1198 }
1199
1200 #if JS_HAS_EXPORT_IMPORT
1201 static JSParseNode *
1202 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1203 0 {
1204 JSParseNode *pn, *pn2;
1205 JSTokenType tt;
1206
1207 0 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1208 0 pn = NewParseNode(cx, ts, PN_NAME, tc);
1209 0 if (!pn)
1210 0 return NULL;
1211 0 pn->pn_op = JSOP_NAME;
1212 0 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1213 0 pn->pn_expr = NULL;
1214 0 pn->pn_slot = -1;
1215 0 pn->pn_attrs = 0;
1216
1217 0 ts->flags |= TSF_OPERAND;
1218 0 while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1219 0 ts->flags &= ~TSF_OPERAND;
1220 0 if (pn->pn_op == JSOP_IMPORTALL)
1221 0 goto bad_import;
1222
1223 0 if (tt == TOK_DOT) {
1224 0 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1225 0 if (!pn2)
1226 0 return NULL;
1227 0 if (js_MatchToken(cx, ts, TOK_STAR)) {
1228 0 pn2->pn_op = JSOP_IMPORTALL;
1229 0 pn2->pn_atom = NULL;
1230 } else {
1231 0 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1232 0 pn2->pn_op = JSOP_GETPROP;
1233 0 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1234 0 pn2->pn_slot = -1;
1235 0 pn2->pn_attrs = 0;
1236 }
1237 0 pn2->pn_expr = pn;
1238 0 pn2->pn_pos.begin = pn->pn_pos.begin;
1239 0 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1240 } else {
1241 /* Make a TOK_LB binary node. */
1242 0 pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc);
1243 0 if (!pn2)
1244 0 return NULL;
1245
1246 0 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1247 }
1248
1249 0 pn = pn2;
1250 0 ts->flags |= TSF_OPERAND;
1251 }
1252 0 ts->flags &= ~TSF_OPERAND;
1253 0 if (tt == TOK_ERROR)
1254 0 return NULL;
1255 0 js_UngetToken(ts);
1256
1257 0 switch (pn->pn_op) {
1258 case JSOP_GETPROP:
1259 0 pn->pn_op = JSOP_IMPORTPROP;
1260 0 break;
1261 case JSOP_GETELEM:
1262 0 pn->pn_op = JSOP_IMPORTELEM;
1263 0 break;
1264 case JSOP_IMPORTALL:
1265 0 break;
1266 default:
1267 0 goto bad_import;
1268 }
1269 0 return pn;
1270
1271 0 bad_import:
1272 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1273 JSMSG_BAD_IMPORT);
1274 0 return NULL;
1275 }
1276 #endif /* JS_HAS_EXPORT_IMPORT */
1277
1278 extern const char js_with_statement_str[];
1279
1280 static JSParseNode *
1281 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1282 602767 {
1283 JSTokenType tt;
1284 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1285 JSStmtInfo stmtInfo, *stmt, *stmt2;
1286 JSAtom *label;
1287
1288 602767 CHECK_RECURSION();
1289
1290 602767 ts->flags |= TSF_OPERAND;
1291 602767 tt = js_GetToken(cx, ts);
1292 602767 ts->flags &= ~TSF_OPERAND;
1293
1294 #if JS_HAS_GETTER_SETTER
1295 602767 if (tt == TOK_NAME) {
1296 478082 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1297 478082 if (tt == TOK_ERROR)
1298 0 return NULL;
1299 }
1300 #endif
1301
1302 602767 switch (tt) {
1303 #if JS_HAS_EXPORT_IMPORT
1304 case TOK_EXPORT:
1305 0 pn = NewParseNode(cx, ts, PN_LIST, tc);
1306 0 if (!pn)
1307 0 return NULL;
1308 0 PN_INIT_LIST(pn);
1309 0 if (js_MatchToken(cx, ts, TOK_STAR)) {
1310 0 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
1311 0 if (!pn2)
1312 0 return NULL;
1313 0 PN_APPEND(pn, pn2);
1314 } else {
1315 do {
1316 0 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1317 0 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1318 0 if (!pn2)
1319 0 return NULL;
1320 0 pn2->pn_op = JSOP_NAME;
1321 0 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1322 0 pn2->pn_expr = NULL;
1323 0 pn2->pn_slot = -1;
1324 0 pn2->pn_attrs = 0;
1325 0 PN_APPEND(pn, pn2);
1326 0 } while (js_MatchToken(cx, ts, TOK_COMMA));
1327 }
1328 0 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1329 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1330 0 break;
1331
1332 case TOK_IMPORT:
1333 0 pn = NewParseNode(cx, ts, PN_LIST, tc);
1334 0 if (!pn)
1335 0 return NULL;
1336 0 PN_INIT_LIST(pn);
1337 do {
1338 0 pn2 = ImportExpr(cx, ts, tc);
1339 0 if (!pn2)
1340 0 return NULL;
1341 0 PN_APPEND(pn, pn2);
1342 0 } while (js_MatchToken(cx, ts, TOK_COMMA));
1343 0 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1344 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1345 0 break;
1346 #endif /* JS_HAS_EXPORT_IMPORT */
1347
1348 case TOK_FUNCTION:
1349 #if JS_HAS_XML_SUPPORT
1350 7659 if (js_PeekToken(cx, ts) == TOK_DBLCOLON)
1351 0 goto expression;
1352 #endif
1353 7659 return FunctionStmt(cx, ts, tc);
1354
1355 case TOK_IF:
1356 /* An IF node has three kids: condition, then, and optional else. */
1357 34237 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
1358 34237 if (!pn)
1359 0 return NULL;
1360 34237 pn1 = Condition(cx, ts, tc);
1361 34237 if (!pn1)
1362 0 return NULL;
1363 34237 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1364 34237 pn2 = Statement(cx, ts, tc);
1365 34237 if (!pn2)
1366 0 return NULL;
1367 34237 if (js_MatchToken(cx, ts, TOK_ELSE)) {
1368 12516 stmtInfo.type = STMT_ELSE;
1369 12516 pn3 = Statement(cx, ts, tc);
1370 12516 if (!pn3)
1371 0 return NULL;
1372 12516 pn->pn_pos.end = pn3->pn_pos.end;
1373 } else {
1374 21721 pn3 = NULL;
1375 21721 pn->pn_pos.end = pn2->pn_pos.end;
1376 }
1377 34237 js_PopStatement(tc);
1378 34237 pn->pn_kid1 = pn1;
1379 34237 pn->pn_kid2 = pn2;
1380 34237 pn->pn_kid3 = pn3;
1381 34237 return pn;
1382
1383 #if JS_HAS_SWITCH_STATEMENT
1384 case TOK_SWITCH:
1385 {
1386 JSParseNode *pn5;
1387 0 JSBool seenDefault = JS_FALSE;
1388
1389 0 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1390 0 if (!pn)
1391 0 return NULL;
1392 0 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1393
1394 /* pn1 points to the switch's discriminant. */
1395 0 pn1 = Expr(cx, ts, tc);
1396 0 if (!pn1)
1397 0 return NULL;
1398
1399 0 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1400 0 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1401
1402 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1403 0 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
1404 0 if (!pn2)
1405 0 return NULL;
1406 0 PN_INIT_LIST(pn2);
1407
1408 0 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1409
1410 0 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1411 0 switch (tt) {
1412 case TOK_DEFAULT:
1413 0 if (seenDefault) {
1414 0 js_ReportCompileErrorNumber(cx, ts,
1415 JSREPORT_TS | JSREPORT_ERROR,
1416 JSMSG_TOO_MANY_DEFAULTS);
1417 0 return NULL;
1418 }
1419 0 seenDefault = JS_TRUE;
1420 /* fall through */
1421
1422 case TOK_CASE:
1423 0 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
1424 0 if (!pn3)
1425 0 return NULL;
1426 0 if (tt == TOK_DEFAULT) {
1427 0 pn3->pn_left = NULL;
1428 } else {
1429 0 pn3->pn_left = Expr(cx, ts, tc);
1430 0 if (!pn3->pn_left)
1431 0 return NULL;
1432 }
1433 0 PN_APPEND(pn2, pn3);
1434 0 if (pn2->pn_count == JS_BIT(16)) {
1435 0 js_ReportCompileErrorNumber(cx, ts,
1436 JSREPORT_TS | JSREPORT_ERROR,
1437 JSMSG_TOO_MANY_CASES);
1438 0 return NULL;
1439 }
1440 break;
1441
1442 case TOK_ERROR:
1443 0 return NULL;
1444
1445 default:
1446 0 js_ReportCompileErrorNumber(cx, ts,
1447 JSREPORT_TS | JSREPORT_ERROR,
1448 JSMSG_BAD_SWITCH);
1449 0 return NULL;
1450 }
1451 0 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1452
1453 0 pn4 = NewParseNode(cx, ts, PN_LIST, tc);
1454 0 if (!pn4)
1455 0 return NULL;
1456 0 pn4->pn_type = TOK_LC;
1457 0 PN_INIT_LIST(pn4);
1458 0 ts->flags |= TSF_OPERAND;
1459 0 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1460 tt != TOK_CASE && tt != TOK_DEFAULT) {
1461 0 ts->flags &= ~TSF_OPERAND;
1462 0 if (tt == TOK_ERROR)
1463 0 return NULL;
1464 0 pn5 = Statement(cx, ts, tc);
1465 0 if (!pn5)
1466 0 return NULL;
1467 0 pn4->pn_pos.end = pn5->pn_pos.end;
1468 0 PN_APPEND(pn4, pn5);
1469 0 ts->flags |= TSF_OPERAND;
1470 }
1471 0 ts->flags &= ~TSF_OPERAND;
1472
1473 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1474 0 if (pn4->pn_head)
1475 0 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1476 0 pn3->pn_pos.end = pn4->pn_pos.end;
1477 0 pn3->pn_right = pn4;
1478 }
1479
1480 0 js_PopStatement(tc);
1481
1482 0 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1483 0 pn->pn_kid1 = pn1;
1484 0 pn->pn_kid2 = pn2;
1485 0 return pn;
1486 }
1487 #endif /* JS_HAS_SWITCH_STATEMENT */
1488
1489 case TOK_WHILE:
1490 700 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1491 700 if (!pn)
1492 0 return NULL;
1493 700 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1494 700 pn2 = Condition(cx, ts, tc);
1495 700 if (!pn2)
1496 0 return NULL;
1497 700 pn->pn_left = pn2;
1498 700 pn2 = Statement(cx, ts, tc);
1499 700 if (!pn2)
1500 0 return NULL;
1501 700 js_PopStatement(tc);
1502 700 pn->pn_pos.end = pn2->pn_pos.end;
1503 700 pn->pn_right = pn2;
1504 700 return pn;
1505
1506 #if JS_HAS_DO_WHILE_LOOP
1507 case TOK_DO:
1508 0 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1509 0 if (!pn)
1510 0 return NULL;
1511 0 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1512 0 pn2 = Statement(cx, ts, tc);
1513 0 if (!pn2)
1514 0 return NULL;
1515 0 pn->pn_left = pn2;
1516 0 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1517 0 pn2 = Condition(cx, ts, tc);
1518 0 if (!pn2)
1519 0 return NULL;
1520 0 js_PopStatement(tc);
1521 0 pn->pn_pos.end = pn2->pn_pos.end;
1522 0 pn->pn_right = pn2;
1523 0 if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) {
1524 /*
1525 * All legacy and extended versions must do automatic semicolon
1526 * insertion after do-while. See the testcase and discussion in
1527 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
1528 */
1529 0 (void) js_MatchToken(cx, ts, TOK_SEMI);
1530 0 return pn;
1531 }
1532 0 break;
1533 #endif /* JS_HAS_DO_WHILE_LOOP */
1534
1535 case TOK_FOR:
1536 /* A FOR node is binary, left is loop control and right is the body. */
1537 4308 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1538 4308 if (!pn)
1539 0 return NULL;
1540 4308 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1541
1542 #if JS_HAS_XML_SUPPORT
1543 4308 pn->pn_op = JSOP_NOP;
1544 4308 if (js_MatchToken(cx, ts, TOK_NAME)) {
1545 0 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
1546 0 pn->pn_op = JSOP_FOREACH;
1547 else
1548 0 js_UngetToken(ts);
1549 }
1550 #endif
1551
1552 4308 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1553 4308 ts->flags |= TSF_OPERAND;
1554 4308 tt = js_PeekToken(cx, ts);
1555 4308 ts->flags &= ~TSF_OPERAND;
1556 4308 if (tt == TOK_SEMI) {
1557 #if JS_HAS_XML_SUPPORT
1558 0 if (pn->pn_op == JSOP_FOREACH)
1559 0 goto bad_for_each;
1560 #endif
1561
1562 /* No initializer -- set first kid of left sub-node to null. */
1563 0 pn1 = NULL;
1564 } else {
1565 /* Set pn1 to a var list or an initializing expression. */
1566 #if JS_HAS_IN_OPERATOR
1567 /*
1568 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1569 * of the for statement. This flag will be used by the RelExpr
1570 * production; if it is set, then the 'in' keyword will not be
1571 * recognized as an operator, leaving it available to be parsed as
1572 * part of a for/in loop. A side effect of this restriction is
1573 * that (unparenthesized) expressions involving an 'in' operator
1574 * are illegal in the init clause of an ordinary for loop.
1575 */
1576 4308 tc->flags |= TCF_IN_FOR_INIT;
1577 #endif /* JS_HAS_IN_OPERATOR */
1578 4308 if (tt == TOK_VAR) {
1579 4308 (void) js_GetToken(cx, ts);
1580 4308 pn1 = Variables(cx, ts, tc);
1581 } else {
1582 0 pn1 = Expr(cx, ts, tc);
1583 }
1584 #if JS_HAS_IN_OPERATOR
1585 4308 tc->flags &= ~TCF_IN_FOR_INIT;
1586 #endif /* JS_HAS_IN_OPERATOR */
1587 4308 if (!pn1)
1588 0 return NULL;
1589 }
1590
1591 /*
1592 * We can be sure that it's a for/in loop if there's still an 'in'
1593 * keyword here, even if JavaScript recognizes 'in' as an operator,
1594 * as we've excluded 'in' from being parsed in RelExpr by setting
1595 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
1596 */
1597 8484 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1598 4176 stmtInfo.type = STMT_FOR_IN_LOOP;
1599
1600 /* Check that the left side of the 'in' is valid. */
1601 8352 while (pn1->pn_type == TOK_RP)
1602 0 pn1 = pn1->pn_kid;
1603 4176 if ((pn1->pn_type == TOK_VAR)
1604 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
1605 : (pn1->pn_type != TOK_NAME &&
1606 pn1->pn_type != TOK_DOT &&
1607 #if JS_HAS_LVALUE_RETURN
1608 pn1->pn_type != TOK_LP &&
1609 #endif
1610 #if JS_HAS_XML_SUPPORT
1611 (pn1->pn_type != TOK_UNARYOP ||
1612 pn1->pn_op != JSOP_XMLNAME) &&
1613 #endif
1614 pn1->pn_type != TOK_LB)) {
1615 0 js_ReportCompileErrorNumber(cx, ts,
1616 JSREPORT_TS | JSREPORT_ERROR,
1617 JSMSG_BAD_FOR_LEFTSIDE);
1618 0 return NULL;
1619 }
1620
1621 4176 if (pn1->pn_type == TOK_VAR) {
1622 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
1623 4176 pn1->pn_extra |= PNX_FORINVAR;
1624
1625 /* Generate a final POP only if the var has an initializer. */
1626 4176 pn2 = pn1->pn_head;
1627 4176 if (pn2->pn_expr)
1628 0 pn1->pn_extra |= PNX_POPVAR;
1629 } else {
1630 0 pn2 = pn1;
1631 #if JS_HAS_LVALUE_RETURN
1632 0 if (pn2->pn_type == TOK_LP)
1633 0 pn2->pn_op = JSOP_SETCALL;
1634 #endif
1635 #if JS_HAS_XML_SUPPORT
1636 0 if (pn2->pn_type == TOK_UNARYOP)
1637 0 pn2->pn_op = JSOP_BINDXMLNAME;
1638 #endif
1639 }
1640
1641 /* Beware 'for (arguments in ...)' with or without a 'var'. */
1642 4176 if (pn2->pn_type == TOK_NAME &&
1643 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1644 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1645 }
1646
1647 /* Parse the object expression as the right operand of 'in'. */
1648 4176 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1649 4176 if (!pn2)
1650 0 return NULL;
1651 4176 pn->pn_left = pn2;
1652 } else {
1653 #if JS_HAS_XML_SUPPORT
1654 132 if (pn->pn_op == JSOP_FOREACH)
1655 0 goto bad_for_each;
1656 #endif
1657
1658 /* Parse the loop condition or null into pn2. */
1659 132 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1660 132 ts->flags |= TSF_OPERAND;
1661 132 tt = js_PeekToken(cx, ts);
1662 132 ts->flags &= ~TSF_OPERAND;
1663 132 if (tt == TOK_SEMI) {
1664 0 pn2 = NULL;
1665 } else {
1666 132 pn2 = Expr(cx, ts, tc);
1667 132 if (!pn2)
1668 0 return NULL;
1669 }
1670
1671 /* Parse the update expression or null into pn3. */
1672 132 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1673 132 ts->flags |= TSF_OPERAND;
1674 132 tt = js_PeekToken(cx, ts);
1675 132 ts->flags &= ~TSF_OPERAND;
1676 132 if (tt == TOK_RP) {
1677 0 pn3 = NULL;
1678 } else {
1679 132 pn3 = Expr(cx, ts, tc);
1680 132 if (!pn3)
1681 0 return NULL;
1682 }
1683
1684 /* Build the RESERVED node to use as the left kid of pn. */
1685 132 pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
1686 132 if (!pn4)
1687 0 return NULL;
1688 132 pn4->pn_type = TOK_RESERVED;
1689 132 pn4->pn_op = JSOP_NOP;
1690 132 pn4->pn_kid1 = pn1;
1691 132 pn4->pn_kid2 = pn2;
1692 132 pn4->pn_kid3 = pn3;
1693 132 pn->pn_left = pn4;
1694 }
1695
1696 4308 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1697
1698 /* Parse the loop body into pn->pn_right. */
1699 4308 pn2 = Statement(cx, ts, tc);
1700 4308 if (!pn2)
1701 0 return NULL;
1702 4308 pn->pn_right = pn2;
1703 4308 js_PopStatement(tc);
1704
1705 /* Record the absolute line number for source note emission. */
1706 4308 pn->pn_pos.end = pn2->pn_pos.end;
1707 4308 return pn;
1708
1709 #if JS_HAS_XML_SUPPORT
1710 0 bad_for_each:
1711 0 js_ReportCompileErrorNumber(cx, pn,
1712 JSREPORT_PN | JSREPORT_ERROR,
1713 JSMSG_BAD_FOR_EACH_LOOP);
1714 0 return NULL;
1715 #endif
1716
1717 #if JS_HAS_EXCEPTIONS
1718 case TOK_TRY: {
1719 66 JSParseNode *catchtail = NULL;
1720 /*
1721 * try nodes are ternary.
1722 * kid1 is the try Statement
1723 * kid2 is the catch node
1724 * kid3 is the finally Statement
1725 *
1726 * catch nodes are ternary.
1727 * kid1 is the discriminant
1728 * kid2 is the next catch node, or NULL
1729 * kid3 is the catch block (on kid3 so that we can always append a
1730 * new catch pn on catchtail->kid2)
1731 *
1732 * catch discriminant nodes are binary
1733 * atom is the receptacle
1734 * expr is the discriminant code
1735 *
1736 * finally nodes are unary (just the finally expression)
1737 */
1738 66 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
1739 66 pn->pn_op = JSOP_NOP;
1740
1741 66 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1742 66 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1743 66 pn->pn_kid1 = Statements(cx, ts, tc);
1744 66 if (!pn->pn_kid1)
1745 0 return NULL;
1746 66 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1747 66 js_PopStatement(tc);
1748
1749 66 catchtail = pn;
1750 198 while (js_PeekToken(cx, ts) == TOK_CATCH) {
1751 /* check for another catch after unconditional catch */
1752 66 if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1753 0 js_ReportCompileErrorNumber(cx, ts,
1754 JSREPORT_TS | JSREPORT_ERROR,
1755 JSMSG_CATCH_AFTER_GENERAL);
1756 0 return NULL;
1757 }
1758
1759 /*
1760 * legal catch forms are:
1761 * catch (v)
1762 * catch (v if <boolean_expression>)
1763 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1764 */
1765 66 (void) js_GetToken(cx, ts); /* eat `catch' */
1766 66 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
1767 66 if (!pn2)
1768 0 return NULL;
1769
1770 /*
1771 * We use a PN_NAME for the discriminant (catchguard) node
1772 * with the actual discriminant code in the initializer spot
1773 */
1774 66 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1775 66 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1776 66 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
1777 66 if (!pn3)
1778 0 return NULL;
1779
1780 66 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1781 66 pn3->pn_expr = NULL;
1782 #if JS_HAS_CATCH_GUARD
1783 /*
1784 * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1785 * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1786 */
1787 66 if (js_PeekToken(cx, ts) == TOK_IF) {
1788 0 (void)js_GetToken(cx, ts); /* eat `if' */
1789 0 pn3->pn_expr = Expr(cx, ts, tc);
1790 0 if (!pn3->pn_expr)
1791 0 return NULL;
1792 }
1793 #endif
1794 66 pn2->pn_kid1 = pn3;
1795
1796 66 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1797
1798 66 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1799 66 js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1800 66 stmtInfo.label = pn3->pn_atom;
1801 66 pn2->pn_kid3 = Statements(cx, ts, tc);
1802 66 if (!pn2->pn_kid3)
1803 0 return NULL;
1804 66 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1805 66 js_PopStatement(tc);
1806
1807 66 catchtail = catchtail->pn_kid2 = pn2;
1808 }
1809 66 catchtail->pn_kid2 = NULL;
1810
1811 66 if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1812 0 tc->tryCount++;
1813 0 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1814 0 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1815 0 pn->pn_kid3 = Statements(cx, ts, tc);
1816 0 if (!pn->pn_kid3)
1817 0 return NULL;
1818 0 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1819 0 js_PopStatement(tc);
1820 } else {
1821 66 pn->pn_kid3 = NULL;
1822 }
1823 66 if (!pn->pn_kid2 && !pn->pn_kid3) {
1824 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1825 JSMSG_CATCH_OR_FINALLY);
1826 0 return NULL;
1827 }
1828 66 tc->tryCount++;
1829 66 return pn;
1830 }
1831
1832 case TOK_THROW:
1833 86 pn = NewParseNode(cx, ts, PN_UNARY, tc);
1834 86 if (!pn)
1835 0 return NULL;
1836
1837 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
1838 86 ts->flags |= TSF_OPERAND;
1839 86 tt = js_PeekTokenSameLine(cx, ts);
1840 86 ts->flags &= ~TSF_OPERAND;
1841 86 if (tt == TOK_ERROR)
1842 0 return NULL;
1843 86 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
1844 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1845 JSMSG_SYNTAX_ERROR);
1846 0 return NULL;
1847 }
1848
1849 86 pn2 = Expr(cx, ts, tc);
1850 86 if (!pn2)
1851 0 return NULL;
1852 86 pn->pn_pos.end = pn2->pn_pos.end;
1853 86 pn->pn_op = JSOP_THROW;
1854 86 pn->pn_kid = pn2;
1855 86 break;
1856
1857 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1858 case TOK_CATCH:
1859 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1860 JSMSG_CATCH_WITHOUT_TRY);
1861 0 return NULL;
1862
1863 case TOK_FINALLY:
1864 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1865 JSMSG_FINALLY_WITHOUT_TRY);
1866 0 return NULL;
1867
1868 #endif /* JS_HAS_EXCEPTIONS */
1869
1870 case TOK_BREAK:
1871 198 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
1872 198 if (!pn)
1873 0 return NULL;
1874 198 if (!MatchLabel(cx, ts, pn))
1875 0 return NULL;
1876 198 stmt = tc->topStmt;
1877 198 label = pn->pn_atom;
1878 198 if (label) {
1879 0 for (; ; stmt = stmt->down) {
1880 0 if (!stmt) {
1881 0 js_ReportCompileErrorNumber(cx, ts,
1882 JSREPORT_TS | JSREPORT_ERROR,
1883 JSMSG_LABEL_NOT_FOUND);
1884 0 return NULL;
1885 }
1886 0 if (stmt->type == STMT_LABEL && stmt->label == label)
1887 0 break;
1888 0 }
1889 } else {
1890 462 for (; ; stmt = stmt->down) {
1891 660 if (!stmt) {
1892 0 js_ReportCompileErrorNumber(cx, ts,
1893 JSREPORT_TS | JSREPORT_ERROR,
1894 JSMSG_TOUGH_BREAK);
1895 0 return NULL;
1896 }
1897 660 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1898 break;
1899 462 }
1900 }
1901 198 if (label)
1902 0 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1903 198 break;
1904
1905 case TOK_CONTINUE:
1906 990 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
1907 990 if (!pn)
1908 0 return NULL;
1909 990 if (!MatchLabel(cx, ts, pn))
1910 0 return NULL;
1911 990 stmt = tc->topStmt;
1912 990 label = pn->pn_atom;
1913 990 if (label) {
1914 0 for (stmt2 = NULL; ; stmt = stmt->down) {
1915 0 if (!stmt) {
1916 0 js_ReportCompileErrorNumber(cx, ts,
1917 JSREPORT_TS | JSREPORT_ERROR,
1918 JSMSG_LABEL_NOT_FOUND);
1919 0 return NULL;
1920 }
1921 0 if (stmt->type == STMT_LABEL) {
1922 0 if (stmt->label == label) {
1923 0 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1924 0 js_ReportCompileErrorNumber(cx, ts,
1925 JSREPORT_TS |
1926 JSREPORT_ERROR,
1927 JSMSG_BAD_CONTINUE);
1928 0 return NULL;
1929 }
1930 break;
1931 }
1932 } else {
1933 0 stmt2 = stmt;
1934 }
1935 0 }
1936 } else {
1937 3300 for (; ; stmt = stmt->down) {
1938 4290 if (!stmt) {
1939 0 js_ReportCompileErrorNumber(cx, ts,
1940 JSREPORT_TS | JSREPORT_ERROR,
1941 JSMSG_BAD_CONTINUE);
1942 0 return NULL;
1943 }
1944 4290 if (STMT_IS_LOOP(stmt))
1945 990 break;
1946 3300 }
1947 }
1948 990 if (label)
1949 0 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1950 990 break;
1951
1952 case TOK_WITH:
1953 0 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1954 0 if (!pn)
1955 0 return NULL;
1956 0 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1957 0 pn2 = Expr(cx, ts, tc);
1958 0 if (!pn2)
1959 0 return NULL;
1960 0 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1961 0 pn->pn_left = pn2;
1962
1963 0 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1964 0 pn2 = Statement(cx, ts, tc);
1965 0 if (!pn2)
1966 0 return NULL;
1967 0 js_PopStatement(tc);
1968
1969 0 pn->pn_pos.end = pn2->pn_pos.end;
1970 0 pn->pn_right = pn2;
1971 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1972 0 return pn;
1973
1974 case TOK_VAR:
1975 23762 pn = Variables(cx, ts, tc);
1976 23762 if (!pn)
1977 0 return NULL;
1978
1979 /* Tell js_EmitTree to generate a final POP. */
1980 23762 pn->pn_extra |= PNX_POPVAR;
1981 23762 break;
1982
1983 case TOK_RETURN:
1984 5082 if (!(tc->flags & TCF_IN_FUNCTION)) {
1985 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1986 JSMSG_BAD_RETURN);
1987 0 return NULL;
1988 }
1989 5082 pn = NewParseNode(cx, ts, PN_UNARY, tc);
1990 5082 if (!pn)
1991 0 return NULL;
1992
1993 /* This is ugly, but we don't want to require a semicolon. */
1994 5082 ts->flags |= TSF_OPERAND;
1995 5082 tt = js_PeekTokenSameLine(cx, ts);
1996 5082 ts->flags &= ~TSF_OPERAND;
1997 5082 if (tt == TOK_ERROR)
1998 0 return NULL;
1999
2000 8382 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2001 3300 pn2 = Expr(cx, ts, tc);
2002 3300 if (!pn2)
2003 0 return NULL;
2004 3300 tc->flags |= TCF_RETURN_EXPR;
2005 3300 pn->pn_pos.end = pn2->pn_pos.end;
2006 3300 pn->pn_kid = pn2;
2007 } else {
2008 1782 tc->flags |= TCF_RETURN_VOID;
2009 1782 pn->pn_kid = NULL;
2010 }
2011
2012 5082 if (JS_HAS_STRICT_OPTION(cx) &&
2013 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
2014 /*
2015 * We must be in a frame with a non-native function, because
2016 * we're compiling one.
2017 */
2018 0 if (!ReportNoReturnValue(cx, ts))
2019 0 return NULL;
2020 }
2021 5082 break;
2022
2023 case TOK_LC:
2024 46646 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
2025 46646 pn = Statements(cx, ts, tc);
2026 46646 if (!pn)
2027 0 return NULL;
2028
2029 46646 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
2030 46646 js_PopStatement(tc);
2031 46646 return pn;
2032
2033 case TOK_EOL:
2034 case TOK_SEMI:
2035 66 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2036 66 if (!pn)
2037 0 return NULL;
2038 66 pn->pn_type = TOK_SEMI;
2039 66 pn->pn_kid = NULL;
2040 66 return pn;
2041
2042 #if JS_HAS_DEBUGGER_KEYWORD
2043 case TOK_DEBUGGER:
2044 0 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
2045 0 if (!pn)
2046 0 return NULL;
2047 0 pn->pn_type = TOK_DEBUGGER;
2048 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2049 0 break;
2050 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2051
2052 #if JS_HAS_XML_SUPPORT
2053 case TOK_DEFAULT:
2054 0 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2055 0 if (!pn)
2056 0 return NULL;
2057 0 if (!js_MatchToken(cx, ts, TOK_NAME) ||
2058 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom ||
2059 !js_MatchToken(cx, ts, TOK_NAME) ||
2060 CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom ||
2061 !js_MatchToken(cx, ts, TOK_ASSIGN) ||
2062 CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2063 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2064 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
2065 0 return NULL;
2066 }
2067 0 pn2 = Expr(cx, ts, tc);
2068 0 if (!pn2)
2069 0 return NULL;
2070 0 pn->pn_op = JSOP_DEFXMLNS;
2071 0 pn->pn_pos.end = pn2->pn_pos.end;
2072 0 pn->pn_kid = pn2;
2073 0 tc->flags |= TCF_HAS_DEFXMLNS;
2074 0 break;
2075 #endif
2076
2077 case TOK_ERROR:
2078 0 return NULL;
2079
2080 default:
2081 #if JS_HAS_XML_SUPPORT
2082 478967 expression:
2083 #endif
2084 478967 js_UngetToken(ts);
2085 478967 pn2 = Expr(cx, ts, tc);
2086 478967 if (!pn2)
2087 0 return NULL;
2088
2089 478967 if (js_PeekToken(cx, ts) == TOK_COLON) {
2090 0 if (pn2->pn_type != TOK_NAME) {
2091 0 js_ReportCompileErrorNumber(cx, ts,
2092 JSREPORT_TS | JSREPORT_ERROR,
2093 JSMSG_BAD_LABEL);
2094 0 return NULL;
2095 }
2096 0 label = pn2->pn_atom;
2097 0 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
2098 0 if (stmt->type == STMT_LABEL && stmt->label == label) {
2099 0 js_ReportCompileErrorNumber(cx, ts,
2100 JSREPORT_TS | JSREPORT_ERROR,
2101 JSMSG_DUPLICATE_LABEL);
2102 0 return NULL;
2103 }
2104 }
2105 0 (void) js_GetToken(cx, ts);
2106
2107 /* Push a label struct and parse the statement. */
2108 0 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
2109 0 stmtInfo.label = label;
2110 0 pn = Statement(cx, ts, tc);
2111 0 if (!pn)
2112 0 return NULL;
2113
2114 /* Pop the label, set pn_expr, and return early. */
2115 0 js_PopStatement(tc);
2116 0 pn2->pn_type = TOK_COLON;
2117 0 pn2->pn_pos.end = pn->pn_pos.end;
2118 0 pn2->pn_expr = pn;
2119 0 return pn2;
2120 }
2121
2122 478967 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2123 478967 if (!pn)
2124 0 return NULL;
2125 478967 pn->pn_type = TOK_SEMI;
2126 478967 pn->pn_pos = pn2->pn_pos;
2127 478967 pn->pn_kid = pn2;
2128 break;
2129 }
2130
2131 /* Check termination of this primitive statement. */
2132 509085 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2133 508506 tt = js_PeekTokenSameLine(cx, ts);
2134 508506 if (tt == TOK_ERROR)
2135 0 return NULL;
2136 508506 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2137 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2138 JSMSG_SEMI_BEFORE_STMNT);
2139 0 return NULL;
2140 }
2141 }
2142
2143 509085 (void) js_MatchToken(cx, ts, TOK_SEMI);
2144 509085 return pn;
2145 }
2146
2147 static JSParseNode *
2148 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2149 28070 {
2150 JSParseNode *pn, *pn2;
2151 JSObject *obj, *pobj;
2152 JSStackFrame *fp;
2153 JSFunction *fun;
2154 JSClass *clasp;
2155 JSPropertyOp getter, setter, currentGetter, currentSetter;
2156 JSAtom *atom;
2157 JSAtomListElement *ale;
2158 JSOp prevop;
2159 JSProperty *prop;
2160 JSScopeProperty *sprop;
2161 JSBool ok;
2162
2163 /*
2164 * The tricky part of this code is to create special parsenode opcodes for
2165 * getting and setting variables (which will be stored as special slots in
2166 * the frame). The complex special case is an eval() inside a function.
2167 * If the evaluated string references variables in the enclosing function,
2168 * then we need to generate the special variable opcodes. We determine
2169 * this by looking up the variable id in the current variable scope.
2170 */
2171 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
2172 28070 pn = NewParseNode(cx, ts, PN_LIST, tc);
2173 28070 if (!pn)
2174 0 return NULL;
2175 28070 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2176 28070 PN_INIT_LIST(pn);
2177
2178 /*
2179 * Skip eval and debugger frames when looking for the function whose code
2180 * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION
2181 * will be set in tc->flags, and we can be sure fp->fun is the function to
2182 * use. But if a function calls eval, the string argument is treated as a
2183 * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
2184 *
2185 * What's more, when the following code is reached from eval, cx->fp->fun
2186 * is eval's JSFunction (a native function), so we need to skip its frame.
2187 * We should find the scripted caller's function frame just below it, but
2188 * we code a loop out of paranoia.
2189 */
2190 28070 for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
2191 continue;
2192 28070 obj = fp->varobj;
2193 28070 fun = fp->fun;
2194 28070 clasp = OBJ_GET_CLASS(cx, obj);
2195 42194 if (fun && clasp == &js_FunctionClass) {
2196 /* We are compiling code inside a function */
2197 14124 getter = js_GetLocalVariable;
2198 14124 setter = js_SetLocalVariable;
2199 13946 } else if (fun && clasp == &js_CallClass) {
2200 /* We are compiling code from an eval inside a function */
2201 0 getter = js_GetCallVariable;
2202 0 setter = js_SetCallVariable;
2203 } else {
2204 13946 getter = clasp->getProperty;
2205 13946 setter = clasp->setProperty;
2206 }
2207
2208 28070 ok = JS_TRUE;
2209 do {
2210 28244 currentGetter = getter;
2211 28244 currentSetter = setter;
2212 28244 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2213 28244 atom = CURRENT_TOKEN(ts).t_atom;
2214
2215 28244 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2216 28244 if (ale) {
2217 9407 prevop = ALE_JSOP(ale);
2218 9407 if (JS_HAS_STRICT_OPTION(cx)
2219 ? pn->pn_op != JSOP_DEFVAR || prevop != JSOP_DEFVAR
2220 : pn->pn_op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) {
2221 0 const char *name = js_AtomToPrintableString(cx, atom);
2222 0 if (!name ||
2223 !js_ReportCompileErrorNumber(cx, ts,
2224 (pn->pn_op != JSOP_DEFCONST &&
2225 prevop != JSOP_DEFCONST)
2226 ? JSREPORT_TS |
2227 JSREPORT_WARNING |
2228 JSREPORT_STRICT
2229 : JSREPORT_TS | JSREPORT_ERROR,
2230 JSMSG_REDECLARED_VAR,
2231 (prevop == JSOP_DEFFUN ||
2232 prevop == JSOP_CLOSURE)
2233 ? js_function_str
2234 : (prevop == JSOP_DEFCONST)
2235 ? js_const_str
2236 : js_var_str,
2237 name)) {
2238 0 return NULL;
2239 }
2240 }
2241 9407 if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2242 0 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2243 } else {
2244 18837 ale = js_IndexAtom(cx, atom, &tc->decls);
2245 18837 if (!ale)
2246 0 return NULL;
2247 }
2248 28244 ALE_SET_JSOP(ale, pn->pn_op);
2249
2250 28244 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2251 28244 if (!pn2)
2252 0 return NULL;
2253 28244 pn2->pn_op = JSOP_NAME;
2254 28244 pn2->pn_atom = atom;
2255 28244 pn2->pn_expr = NULL;
2256 28244 pn2->pn_slot = -1;
2257 28244 pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2258 ? JSPROP_PERMANENT | JSPROP_READONLY
2259 : JSPROP_PERMANENT;
2260 28244 PN_APPEND(pn, pn2);
2261
2262 28244 if (!fun) {
2263 /* Don't lookup global variables at compile time. */
2264 373 prop = NULL;
2265 55742 } else if (OBJ_IS_NATIVE(obj)) {
2266 27871 if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
2267 &pobj, &prop)) {
2268 0 return NULL;
2269 }
2270 } else {
2271 0 if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
2272 0 return NULL;
2273 }
2274 31082 if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2275 2838 sprop = (JSScopeProperty *)prop;
2276 2838 if (sprop->getter == js_GetArgument) {
2277 0 const char *name = js_AtomToPrintableString(cx, atom);
2278 0 if (!name) {
2279 0 ok = JS_FALSE;
2280 0 } else if (pn->pn_op == JSOP_DEFCONST) {
2281 0 js_ReportCompileErrorNumber(cx, ts,
2282 JSREPORT_TS | JSREPORT_ERROR,
2283 JSMSG_REDECLARED_PARAM,
2284 name);
2285 0 ok = JS_FALSE;
2286 } else {
2287 0 currentGetter = js_GetArgument;
2288 0 currentSetter = js_SetArgument;
2289 0 ok = js_ReportCompileErrorNumber(cx, ts,
2290 JSREPORT_TS |
2291 JSREPORT_WARNING |
2292 JSREPORT_STRICT,
2293 JSMSG_VAR_HIDES_ARG,
2294 name);
2295 }
2296 } else {
2297 2838 if (fun) {
2298 /* Not an argument, must be a redeclared local var. */
2299 2838 if (clasp == &js_FunctionClass) {
2300 JS_ASSERT(sprop->getter == js_GetLocalVariable);
2301 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2302 (uint16) sprop->shortid < fun->nvars);
2303 0 } else if (clasp == &js_CallClass) {
2304 0 if (sprop->getter == js_GetCallVariable) {
2305 /*
2306 * Referencing a variable introduced by a var
2307 * statement in the enclosing function. Check
2308 * that the slot number we have is in range.
2309 */
2310 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2311 (uint16) sprop->shortid < fun->nvars);
2312 } else {
2313 /*
2314 * A variable introduced through another eval:
2315 * don't use the special getters and setters
2316 * since we can't allocate a slot in the frame.
2317 */
2318 0 currentGetter = sprop->getter;
2319 0 currentSetter = sprop->setter;
2320 }
2321 }
2322
2323 /* Override the old getter and setter, to handle eval. */
2324 2838 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2325 0, sprop->attrs,
2326 currentGetter,
2327 currentSetter);
2328 2838 if (!sprop)
2329 0 ok = JS_FALSE;
2330 }
2331 }
2332 } else {
2333 /*
2334 * Property not found in current variable scope: we have not seen
2335 * this variable before. Define a new local variable by adding a
2336 * property to the function's scope, allocating one slot in the
2337 * function's frame. Global variables and any locals declared in
2338 * with statement bodies are handled at runtime, by script prolog
2339 * JSOP_DEFVAR bytecodes generated for slot-less vars.
2340 */
2341 25406 sprop = NULL;
2342 25406 if (prop) {
2343 0 OBJ_DROP_PROPERTY(cx, pobj, prop);
2344 0 prop = NULL;
2345 }
2346 25406 if (currentGetter == js_GetCallVariable) {
2347 /* Can't increase fun->nvars in an active frame! */
2348 0 currentGetter = clasp->getProperty;
2349 0 currentSetter = clasp->setProperty;
2350 }
2351 25406 if (currentGetter == js_GetLocalVariable &&
2352 atom != cx->runtime->atomState.argumentsAtom &&
2353 fp->scopeChain == obj &&
2354 !js_InWithStatement(tc)) {
2355 11286 if (!js_AddHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
2356 currentGetter, currentSetter,
2357 SPROP_INVALID_SLOT,
2358 pn2->pn_attrs | JSPROP_SHARED,
2359 SPROP_HAS_SHORTID, fun->nvars)) {
2360 0 return NULL;
2361 }
2362 11286 if (fun->nvars == JS_BITMASK(16)) {
2363 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2364 JSMSG_TOO_MANY_FUN_VARS);
2365 0 return NULL;
2366 }
2367 11286 fun->nvars++;
2368 }
2369 }
2370
2371 28244 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2372 23029 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2373 0 js_ReportCompileErrorNumber(cx, ts,
2374 JSREPORT_TS | JSREPORT_ERROR,
2375 JSMSG_BAD_VAR_INIT);
2376 0 ok = JS_FALSE;
2377 } else {
2378 23029 pn2->pn_expr = AssignExpr(cx, ts, tc);
2379 23029 if (!pn2->pn_expr) {
2380 0 ok = JS_FALSE;
2381 } else {
2382 23029 pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2383 ? JSOP_SETCONST
2384 : JSOP_SETNAME;
2385 23029 if (atom == cx->runtime->atomState.argumentsAtom)
2386 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2387 }
2388 }
2389 }
2390
2391 28244 if (prop)
2392 2838 OBJ_DROP_PROPERTY(cx, pobj, prop);
2393 28244 if (!ok)
2394 0 return NULL;
2395 28244 } while (js_MatchToken(cx, ts, TOK_COMMA));
2396
2397 28070 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2398 28070 return pn;
2399 }
2400
2401 static JSParseNode *
2402 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2403 573796 {
2404 JSParseNode *pn, *pn2;
2405
2406 573796 pn = AssignExpr(cx, ts, tc);
2407 573796 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2408 0 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2409 0 if (!pn2)
2410 0 return NULL;
2411 0 pn2->pn_pos.begin = pn->pn_pos.begin;
2412 0 PN_INIT_LIST_1(pn2, pn);
2413 0 pn = pn2;
2414 do {
2415 0 pn2 = AssignExpr(cx, ts, tc);
2416 0 if (!pn2)
2417 0 return NULL;
2418 0 PN_APPEND(pn, pn2);
2419 0 } while (js_MatchToken(cx, ts, TOK_COMMA));
2420 0 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2421 }
2422 573796 return pn;
2423 }
2424
2425 static JSParseNode *
2426 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2427 1164651 {
2428 JSParseNode *pn, *pn2;
2429 JSTokenType tt;
2430 JSOp op;
2431
2432 1164651 CHECK_RECURSION();
2433
2434 1164651 pn = CondExpr(cx, ts, tc);
2435 1164651 if (!pn)
2436 0 return NULL;
2437
2438 1164651 tt = js_GetToken(cx, ts);
2439 #if JS_HAS_GETTER_SETTER
2440 1164651 if (tt == TOK_NAME) {
2441 0 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2442 0 if (tt == TOK_ERROR)
2443 0 return NULL;
2444 }
2445 #endif
2446 1164651 if (tt != TOK_ASSIGN) {
2447 1139501 js_UngetToken(ts);
2448 1139501 return pn;
2449 }
2450
2451 25150 op = CURRENT_TOKEN(ts).t_op;
2452 25150 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2453 continue;
2454 25150 switch (pn2->pn_type) {
2455 case TOK_NAME:
2456 17522 pn2->pn_op = JSOP_SETNAME;
2457 17522 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2458 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2459 17522 break;
2460 case TOK_DOT:
2461 1479 pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD)
2462 ? JSOP_SETMETHOD
2463 : JSOP_SETPROP;
2464 1479 break;
2465 case TOK_LB:
2466 6149 pn2->pn_op = JSOP_SETELEM;
2467 6149 break;
2468 #if JS_HAS_LVALUE_RETURN
2469 case TOK_LP:
2470 0 pn2->pn_op = JSOP_SETCALL;
2471 0 break;
2472 #endif
2473 #if JS_HAS_XML_SUPPORT
2474 case TOK_UNARYOP:
2475 0 if (pn2->pn_op == JSOP_XMLNAME) {
2476 0 pn2->pn_op = JSOP_SETXMLNAME;
2477 0 break;
2478 }
2479 /* FALL THROUGH */
2480 #endif
2481 default:
2482 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2483 JSMSG_BAD_LEFTSIDE_OF_ASS);
2484 0 return NULL;
2485 }
2486 25150 pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2487 25150 return pn;
2488 }
2489
2490 static JSParseNode *
2491 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2492 1164651 {
2493 JSParseNode *pn, *pn1, *pn2, *pn3;
2494 #if JS_HAS_IN_OPERATOR
2495 uintN oldflags;
2496 #endif /* JS_HAS_IN_OPERATOR */
2497
2498 1164651 pn = OrExpr(cx, ts, tc);
2499 1164651 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2500 998 pn1 = pn;
2501 998 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2502 998 if (!pn)
2503 0 return NULL;
2504 #if JS_HAS_IN_OPERATOR
2505 /*
2506 * Always accept the 'in' operator in the middle clause of a ternary,
2507 * where it's unambiguous, even if we might be parsing the init of a
2508 * for statement.
2509 */
2510 998 oldflags = tc->flags;
2511 998 tc->flags &= ~TCF_IN_FOR_INIT;
2512 #endif /* JS_HAS_IN_OPERATOR */
2513 998 pn2 = AssignExpr(cx, ts, tc);
2514 #if JS_HAS_IN_OPERATOR
2515 998 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2516 #endif /* JS_HAS_IN_OPERATOR */
2517
2518 998 if (!pn2)
2519 0 return NULL;
2520 998 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2521 998 pn3 = AssignExpr(cx, ts, tc);
2522 998 if (!pn3)
2523 0 return NULL;
2524 998 pn->pn_pos.begin = pn1->pn_pos.begin;
2525 998 pn->pn_pos.end = pn3->pn_pos.end;
2526 998 pn->pn_kid1 = pn1;
2527 998 pn->pn_kid2 = pn2;
2528 998 pn->pn_kid3 = pn3;
2529 }
2530 1164651 return pn;
2531 }
2532
2533 static JSParseNode *
2534 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2535 1169330 {
2536 JSParseNode *pn;
2537
2538 1169330 pn = AndExpr(cx, ts, tc);
2539 1169330 if (pn && js_MatchToken(cx, ts, TOK_OR))
2540 4679 pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2541 1169330 return pn;
2542 }
2543
2544 static JSParseNode *
2545 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2546 1177244 {
2547 JSParseNode *pn;
2548
2549 1177244 pn = BitOrExpr(cx, ts, tc);
2550 1177244 if (pn && js_MatchToken(cx, ts, TOK_AND))
2551 7914 pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2552 1177244 return pn;
2553 }
2554
2555 static JSParseNode *
2556 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2557 1177244 {
2558 JSParseNode *pn;
2559
2560 1177244 pn = BitXorExpr(cx, ts, tc);
2561 2354488 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2562 0 pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2563 tc);
2564 }
2565 1177244 return pn;
2566 }
2567
2568 static JSParseNode *
2569 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2570 1177244 {
2571 JSParseNode *pn;
2572
2573 1177244 pn = BitAndExpr(cx, ts, tc);
2574 2354488 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2575 0 pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2576 tc);
2577 }
2578 1177244 return pn;
2579 }
2580
2581 static JSParseNode *
2582 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2583 1177244 {
2584 JSParseNode *pn;
2585
2586 1177244 pn = EqExpr(cx, ts, tc);
2587 2354488 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2588 0 pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2589 1177244 return pn;
2590 }
2591
2592 static JSParseNode *
2593 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2594 1177244 {
2595 JSParseNode *pn;
2596 JSOp op;
2597
2598 1177244 pn = RelExpr(cx, ts, tc);
2599 2392714 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2600 38226 op = CURRENT_TOKEN(ts).t_op;
2601 38226 pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2602 }
2603 1177244 return pn;
2604 }
2605
2606 static JSParseNode *
2607 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2608 1215470 {
2609 JSParseNode *pn;
2610 JSTokenType tt;
2611 JSOp op;
2612 #if JS_HAS_IN_OPERATOR
2613 1215470 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2614
2615 /*
2616 * Uses of the in operator in ShiftExprs are always unambiguous,
2617 * so unset the flag that prohibits recognizing it.
2618 */
2619 1215470 tc->flags &= ~TCF_IN_FOR_INIT;
2620 #endif /* JS_HAS_IN_OPERATOR */
2621
2622 1215470 pn = ShiftExpr(cx, ts, tc);
2623 2431930 while (pn &&
2624 (js_MatchToken(cx, ts, TOK_RELOP)
2625 #if JS_HAS_IN_OPERATOR
2626 /*
2627 * Recognize the 'in' token as an operator only if we're not
2628 * currently in the init expr of a for loop.
2629 */
2630 || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
2631 #endif /* JS_HAS_IN_OPERATOR */
2632 #if JS_HAS_INSTANCEOF
2633 || js_MatchToken(cx, ts, TOK_INSTANCEOF)
2634 #endif /* JS_HAS_INSTANCEOF */
2635 )) {
2636 990 tt = CURRENT_TOKEN(ts).type;
2637 990 op = CURRENT_TOKEN(ts).t_op;
2638 990 pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
2639 }
2640 #if JS_HAS_IN_OPERATOR
2641 /* Restore previous state of inForInit flag. */
2642 1215470 tc->flags |= inForInitFlag;
2643 #endif /* JS_HAS_IN_OPERATOR */
2644
2645 1215470 return pn;
2646 }
2647
2648 static JSParseNode *
2649 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2650 1216460 {
2651 JSParseNode *pn;
2652 JSOp op;
2653
2654 1216460 pn = AddExpr(cx, ts, tc);
2655 2432920 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2656 0 op = CURRENT_TOKEN(ts).t_op;
2657 0 pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2658 }
2659 1216460 return pn;
2660 }
2661
2662 static JSParseNode *
2663 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2664 1216460 {
2665 JSParseNode *pn;
2666 JSTokenType tt;
2667 JSOp op;
2668
2669 1216460 pn = MulExpr(cx, ts, tc);
2670 2456145 while (pn &&
2671 (js_MatchToken(cx, ts, TOK_PLUS) ||
2672 js_MatchToken(cx, ts, TOK_MINUS))) {
2673 23225 tt = CURRENT_TOKEN(ts).type;
2674 23225 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2675 23225 pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2676 }
2677 1216460 return pn;
2678 }
2679
2680 static JSParseNode *
2681 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2682 1239685 {
2683 JSParseNode *pn;
2684 JSTokenType tt;
2685 JSOp op;
2686
2687 1239685 pn = UnaryExpr(cx, ts, tc);
2688 2479370 while (pn &&
2689 (js_MatchToken(cx, ts, TOK_STAR) ||
2690 js_MatchToken(cx, ts, TOK_DIVOP))) {
2691 0 tt = CURRENT_TOKEN(ts).type;
2692 0 op = CURRENT_TOKEN(ts).t_op;
2693 0 pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2694 }
2695 1239685 return pn;
2696 }
2697
2698 static JSParseNode *
2699 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2700 const char *name)
2701 858 {
2702 1716 while (kid->pn_type == TOK_RP)
2703 0 kid = kid->pn_kid;
2704 858 if (kid->pn_type != TOK_NAME &&
2705 kid->pn_type != TOK_DOT &&
2706 #if JS_HAS_LVALUE_RETURN
2707 (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
2708 #endif
2709 #if JS_HAS_XML_SUPPORT
2710 (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) &&
2711 #endif
2712 kid->pn_type != TOK_LB) {
2713 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2714 JSMSG_BAD_OPERAND, name);
2715 0 return NULL;
2716 }
2717 858 pn->pn_kid = kid;
2718 858 return kid;
2719 }
2720
2721 static const char *incop_name_str[] = {"increment", "decrement"};
2722
2723 static JSBool
2724 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2725 JSParseNode *pn, JSParseNode *kid,
2726 JSTokenType tt, JSBool preorder)
2727 858 {
2728 JSOp op;
2729
2730 858 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2731 858 if (!kid)
2732 0 return JS_FALSE;
2733 858 switch (kid->pn_type) {
2734 case TOK_NAME:
2735 264 op = (tt == TOK_INC)
2736 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2737 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2738 264 if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2739 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2740 264 break;
2741
2742 case TOK_DOT:
2743 0 op = (tt == TOK_INC)
2744 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2745 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2746 0 break;
2747
2748 #if JS_HAS_LVALUE_RETURN
2749 case TOK_LP:
2750 JS_ASSERT(kid->pn_op == JSOP_CALL);
2751 0 kid->pn_op = JSOP_SETCALL;
2752 /* FALL THROUGH */
2753 #endif
2754 #if JS_HAS_XML_SUPPORT
2755 case TOK_UNARYOP:
2756 0 if (kid->pn_op == JSOP_XMLNAME)
2757 0 kid->pn_op = JSOP_SETXMLNAME;
2758 /* FALL THROUGH */
2759 #endif
2760 case TOK_LB:
2761 594 op = (tt == TOK_INC)
2762 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2763 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2764 594 break;
2765
2766 default:
2767 JS_ASSERT(0);
2768 0 op = JSOP_NOP;
2769 }
2770 858 pn->pn_op = op;
2771 858 return JS_TRUE;
2772 }
2773
2774 static JSParseNode *
2775 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2776 1241937 {
2777 JSTokenType tt;
2778 JSParseNode *pn, *pn2;
2779
2780 1241937 CHECK_RECURSION();
2781
2782 1241937 ts->flags |= TSF_OPERAND;
2783 1241937 tt = js_GetToken(cx, ts);
2784 1241937 ts->flags &= ~TSF_OPERAND;
2785
2786 1241937 switch (tt) {
2787 case TOK_UNARYOP:
2788 case TOK_PLUS:
2789 case TOK_MINUS:
2790 2252 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2791 2252 if (!pn)
2792 0 return NULL;
2793 2252 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
2794 2252 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2795 2252 pn2 = UnaryExpr(cx, ts, tc);
2796 2252 if (!pn2)
2797 0 return NULL;
2798 2252 pn->pn_pos.end = pn2->pn_pos.end;
2799 2252 pn->pn_kid = pn2;
2800 2252 break;
2801
2802 case TOK_INC:
2803 case TOK_DEC:
2804 198 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2805 198 if (!pn)
2806 0 return NULL;
2807 198 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2808 198 if (!pn2)
2809 0 return NULL;
2810 198 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2811 0 return NULL;
2812 198 pn->pn_pos.end = pn2->pn_pos.end;
2813 198 break;
2814
2815 case TOK_DELETE:
2816 0 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2817 0 if (!pn)
2818 0 return NULL;
2819 0 pn2 = UnaryExpr(cx, ts, tc);
2820 0 if (!pn2)
2821 0 return NULL;
2822 0 pn->pn_pos.end = pn2->pn_pos.end;
2823
2824 /*
2825 * Under ECMA3, deleting any unary expression is valid -- it simply
2826 * returns true. Here we strip off any parentheses.
2827 */
2828 0 while (pn2->pn_type == TOK_RP)
2829 0 pn2 = pn2->pn_kid;
2830 0 pn->pn_kid = pn2;
2831 0 break;
2832
2833 case TOK_ERROR:
2834 0 return NULL;
2835
2836 default:
2837 1239487 js_UngetToken(ts);
2838 1239487 pn = MemberExpr(cx, ts, tc, JS_TRUE);
2839 1239487 if (!pn)
2840 0 return NULL;
2841
2842 /* Don't look across a newline boundary for a postfix incop. */
2843 1239487 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2844 1238458 tt = js_PeekTokenSameLine(cx, ts);
2845 1238458 if (tt == TOK_INC || tt == TOK_DEC) {
2846 660 (void) js_GetToken(cx, ts);
2847 660 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
2848 660 if (!pn2)
2849 0 return NULL;
2850 660 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2851 0 return NULL;
2852 660 pn2->pn_pos.begin = pn->pn_pos.begin;
2853 660 pn = pn2;
2854 }
2855 }
2856 break;
2857 }
2858 1241937 return pn;
2859 }
2860
2861 static JSBool
2862 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2863 JSParseNode *listNode)
2864 480169 {
2865 JSBool matched;
2866
2867 480169 ts->flags |= TSF_OPERAND;
2868 480169 matched = js_MatchToken(cx, ts, TOK_RP);
2869 480169 ts->flags &= ~TSF_OPERAND;
2870 480169 if (!matched) {
2871 do {
2872 505513 JSParseNode *argNode = AssignExpr(cx, ts, tc);
2873 505513 if (!argNode)
2874 0 return JS_FALSE;
2875 505513 PN_APPEND(listNode, argNode);
2876 505513 } while (js_MatchToken(cx, ts, TOK_COMMA));
2877
2878 474126 if (js_GetToken(cx, ts) != TOK_RP) {
2879 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2880 JSMSG_PAREN_AFTER_ARGS);
2881 0 return JS_FALSE;
2882 }
2883 }
2884 480169 return JS_TRUE;
2885 }
2886
2887 static JSParseNode *
2888 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2889 JSBool allowCallSyntax)
2890 1241862 {
2891 JSParseNode *pn, *pn2, *pn3;
2892 JSTokenType tt;
2893
2894 1241862 CHECK_RECURSION();
2895
2896 /* Check for new expression first. */
2897 1241862 ts->flags |= TSF_OPERAND;
2898 1241862 tt = js_PeekToken(cx, ts);
2899 1241862 ts->flags &= ~TSF_OPERAND;
2900 1241862 if (tt == TOK_NEW) {
2901 2177 (void) js_GetToken(cx, ts);
2902
2903 2177 pn = NewParseNode(cx, ts, PN_LIST, tc);
2904 2177 if (!pn)
2905 0 return NULL;
2906 2177 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2907 2177 if (!pn2)
2908 0 return NULL;
2909 2177 pn->pn_op = JSOP_NEW;
2910 2177 PN_INIT_LIST_1(pn, pn2);
2911 2177 pn->pn_pos.begin = pn2->pn_pos.begin;
2912
2913 2177 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2914 0 return NULL;
2915 2177 if (pn->pn_count > ARGC_LIMIT) {
2916 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2917 JSMSG_TOO_MANY_CON_ARGS);
2918 0 return NULL;
2919 }
2920 2177 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2921 } else {
2922 1239685 pn = PrimaryExpr(cx, ts, tc);
2923 1239685 if (!pn)
2924 0 return NULL;
2925
2926 1239685 if (pn->pn_type == TOK_ANYNAME ||
2927 pn->pn_type == TOK_AT ||
2928 pn->pn_type == TOK_DBLCOLON) {
2929 0 pn2 = NewOrRecycledNode(cx, tc);
2930 0 if (!pn2)
2931 0 return NULL;
2932 0 pn2->pn_type = TOK_UNARYOP;
2933 0 pn2->pn_pos = pn->pn_pos;
2934 0 pn2->pn_op = JSOP_XMLNAME;
2935 0 pn2->pn_arity = PN_UNARY;
2936 0 pn2->pn_kid = pn;
2937 0 pn2->pn_next = NULL;
2938 #if JS_HAS_XML_SUPPORT
2939 0 pn2->pn_ts = ts;
2940 #endif
2941 0 pn = pn2;
2942 }
2943 }
2944
2945 3103351 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2946 1861489 if (tt == TOK_DOT) {
2947 91500 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2948 91500 if (!pn2)
2949 0 return NULL;
2950 #if JS_HAS_XML_SUPPORT
2951 91500 pn3 = PrimaryExpr(cx, ts, tc);
2952 91500 if (!pn3)
2953 0 return NULL;
2954 91500 tt = pn3->pn_type;
2955 183000 if (tt == TOK_NAME ||
2956 (tt == TOK_DBLCOLON &&
2957 pn3->pn_arity == PN_NAME &&
2958 pn3->pn_expr->pn_type == TOK_FUNCTION)) {
2959 91500 pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD;
2960 91500 pn2->pn_expr = pn;
2961 91500 pn2->pn_atom = pn3->pn_atom;
2962 91500 RecycleTree(pn3, tc);
2963 } else {
2964 0 if (TOKEN_TYPE_IS_XML(tt)) {
2965 0 pn2->pn_type = TOK_LB;
2966 0 pn2->pn_op = JSOP_GETELEM;
2967 0 } else if (tt == TOK_RP) {
2968 0 JSParseNode *group = pn3;
2969
2970 /* Recycle the useless TOK_RP/JSOP_GROUP node. */
2971 0 pn3 = group->pn_kid;
2972 0 group->pn_kid = NULL;
2973 0 RecycleTree(group, tc);
2974 0 pn2->pn_type = TOK_FILTER;
2975 0 pn2->pn_op = JSOP_FILTER;
2976 } else {
2977 0 js_ReportCompileErrorNumber(cx, ts,
2978 JSREPORT_TS | JSREPORT_ERROR,
2979 JSMSG_NAME_AFTER_DOT);
2980 0 return NULL;
2981 }
2982 0 pn2->pn_arity = PN_BINARY;
2983 0 pn2->pn_left = pn;
2984 0 pn2->pn_right = pn3;
2985 }
2986 #else
2987 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
2988 pn2->pn_op = JSOP_GETPROP;
2989 pn2->pn_expr = pn;
2990 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2991 #endif
2992 91500 pn2->pn_pos.begin = pn->pn_pos.begin;
2993 91500 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2994 #if JS_HAS_XML_SUPPORT
2995 1769989 } else if (tt == TOK_DBLDOT) {
2996 0 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
2997 0 if (!pn2)
2998 0 return NULL;
2999 0 pn3 = PrimaryExpr(cx, ts, tc);
3000 0 if (!pn3)
3001 0 return NULL;
3002 0 tt = pn3->pn_type;
3003 0 if (tt == TOK_NAME) {
3004 0 pn3->pn_type = TOK_STRING;
3005 0 pn3->pn_arity = PN_NULLARY;
3006 0 pn3->pn_op = JSOP_STRING;
3007 0 } else if (!TOKEN_TYPE_IS_XML(tt)) {
3008 0 js_ReportCompileErrorNumber(cx, ts,
3009 JSREPORT_TS | JSREPORT_ERROR,
3010 JSMSG_NAME_AFTER_DOT);
3011 0 return NULL;
3012 }
3013 0 pn2->pn_op = JSOP_DESCENDANTS;
3014 0 pn2->pn_left = pn;
3015 0 pn2->pn_right = pn3;
3016 0 pn2->pn_pos.begin = pn->pn_pos.begin;
3017 0 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3018 #endif
3019 1769989 } else if (tt == TOK_LB) {
3020 50135 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
3021 50135 if (!pn2)
3022 0 return NULL;
3023 50135 pn3 = Expr(cx, ts, tc);
3024 50135 if (!pn3)
3025 0 return NULL;
3026
3027 50135 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
3028 50135 pn2->pn_pos.begin = pn->pn_pos.begin;
3029 50135 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3030
3031 /* Optimize o['p'] to o.p by rewriting pn2. */
3032 50135 if (pn3->pn_type == TOK_STRING) {
3033 1188 pn2->pn_type = TOK_DOT;
3034 1188 pn2->pn_op = JSOP_GETPROP;
3035 1188 pn2->pn_arity = PN_NAME;
3036 1188 pn2->pn_expr = pn;
3037 1188 pn2->pn_atom = pn3->pn_atom;
3038 } else {
3039 48947 pn2->pn_op = JSOP_GETELEM;
3040 48947 pn2->pn_left = pn;
3041 48947 pn2->pn_right = pn3;
3042 }
3043 2197846 } else if (allowCallSyntax && tt == TOK_LP) {
3044 477992 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
3045 477992 if (!pn2)
3046 0 return NULL;
3047
3048 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
3049 477992 pn2->pn_op = JSOP_CALL;
3050 477992 if (pn->pn_op == JSOP_NAME &&
3051 pn->pn_atom == cx->runtime->atomState.evalAtom) {
3052 0 pn2->pn_op = JSOP_EVAL;
3053 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3054 }
3055
3056 477992 PN_INIT_LIST_1(pn2, pn);
3057 477992 pn2->pn_pos.begin = pn->pn_pos.begin;
3058
3059 477992 if (!ArgumentList(cx, ts, tc, pn2))
3060 0 return NULL;
3061 477992 if (pn2->pn_count > ARGC_LIMIT) {
3062 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3063 JSMSG_TOO_MANY_FUN_ARGS);
3064 0 return NULL;
3065 }
3066 477992 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3067 } else {
3068 1241862 js_UngetToken(ts);
3069 1241862 return pn;
3070 }
3071
3072 619627 pn = pn2;
3073 }
3074 0 if (tt == TOK_ERROR)
3075 0 return NULL;
3076 0 return pn;
3077 }
3078
3079 static JSParseNode *
3080 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3081 1931 {
3082 uintN oldflags;
3083 JSParseNode *pn;
3084
3085 #if JS_HAS_IN_OPERATOR
3086 /*
3087 * Always accept the 'in' operator in a parenthesized expression,
3088 * where it's unambiguous, even if we might be parsing the init of a
3089 * for statement.
3090 */
3091 1931 oldflags = tc->flags;
3092 1931 tc->flags &= ~TCF_IN_FOR_INIT;
3093 #endif
3094 1931 pn = Expr(cx, ts, tc);
3095 #if JS_HAS_IN_OPERATOR
3096 1931 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3097 #endif
3098 1931 return pn;
3099 }
3100
3101 #if JS_HAS_XML_SUPPORT
3102
3103 static JSParseNode *
3104 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3105 0 {
3106 JSParseNode *pn;
3107
3108 0 pn = BracketedExpr(cx, ts, tc);
3109 0 if (!pn)
3110 0 return NULL;
3111
3112 0 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
3113 0 return pn;
3114 }
3115
3116 /*
3117 * From the ECMA-357 grammar in 11.1.1 and 11.1.2:
3118 *
3119 * AttributeIdentifier:
3120 * @ PropertySelector
3121 * @ QualifiedIdentifier
3122 * @ [ Expression ]
3123 *
3124 * PropertySelector:
3125 * Identifier
3126 * *
3127 *
3128 * QualifiedIdentifier:
3129 * PropertySelector :: PropertySelector
3130 * PropertySelector :: [ Expression ]
3131 *
3132 * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so:
3133 *
3134 * AttributeIdentifier:
3135 * @ QualifiedIdentifier
3136 * @ [ Expression ]
3137 *
3138 * PropertySelector:
3139 * Identifier
3140 * *
3141 *
3142 * QualifiedIdentifier:
3143 * PropertySelector :: PropertySelector
3144 * PropertySelector :: [ Expression ]
3145 * PropertySelector
3146 *
3147 * Since PrimaryExpression: Identifier in ECMA-262 and we want the semantics
3148 * for that rule to result in a name node, but extend the grammar to include
3149 * PrimaryExpression: QualifiedIdentifier, we factor further:
3150 *
3151 * QualifiedIdentifier:
3152 * PropertySelector QualifiedSuffix
3153 *
3154 * QualifiedSuffix:
3155 * :: PropertySelector
3156 * :: [ Expression ]
3157 * /nothing/
3158 *
3159 * And use this production instead of PrimaryExpression: QualifiedIdentifier:
3160 *
3161 * PrimaryExpression:
3162 * Identifier QualifiedSuffix
3163 *
3164 * We hoists the :: match into callers of QualifiedSuffix, in order to tweak
3165 * PropertySelector vs. Identifier pn_arity, pn_op, and other members.
3166 */
3167 static JSParseNode *
3168 PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3169 0 {
3170 JSParseNode *pn;
3171
3172 0 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3173 0 if (!pn)
3174 0 return NULL;
3175 0 if (pn->pn_type == TOK_STAR) {
3176 0 pn->pn_type = TOK_ANYNAME;
3177 0 pn->pn_op = JSOP_ANYNAME;
3178 0 pn->pn_atom = cx->runtime->atomState.starAtom;
3179 } else {
3180 JS_ASSERT(pn->pn_type == TOK_NAME);
3181 0 pn->pn_op = JSOP_QNAMEPART;
3182 0 pn->pn_arity = PN_NAME;
3183 0 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3184 0 pn->pn_expr = NULL;
3185 0 pn->pn_slot = -1;
3186 0 pn->pn_attrs = 0;
3187 }
3188 0 return pn;
3189 }
3190
3191 static JSParseNode *
3192 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
3193 JSTreeContext *tc)
3194 0 {
3195 JSParseNode *pn2, *pn3;
3196 JSTokenType tt;
3197
3198 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
3199 0 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
3200 0 if (!pn2)
3201 0 return NULL;
3202
3203 /* Left operand of :: must be evaluated if it is an identifier. */
3204 0 if (pn->pn_op == JSOP_QNAMEPART)
3205 0 pn->pn_op = JSOP_NAME;
3206
3207 0 tt = js_GetToken(cx, ts);
3208 0 if (tt == TOK_STAR || tt == TOK_NAME) {
3209 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
3210 0 pn2->pn_op = JSOP_QNAMECONST;
3211 0 pn2->pn_atom = (tt == TOK_STAR)
3212 ? cx->runtime->atomState.starAtom
3213 : CURRENT_TOKEN(ts).t_atom;
3214 0 pn2->pn_expr = pn;
3215 0 pn2->pn_slot = -1;
3216 0 pn2->pn_attrs = 0;
3217 0 return pn2;
3218 }
3219
3220 0 if (tt != TOK_LB) {
3221 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3222 JSMSG_SYNTAX_ERROR);
3223 0 return NULL;
3224 }
3225 0 pn3 = EndBracketedExpr(cx, ts, tc);
3226 0 if (!pn3)
3227 0 return NULL;
3228
3229 0 pn2->pn_op = JSOP_QNAME;
3230 0 pn2->pn_arity = PN_BINARY;
3231 0 pn2->pn_left = pn;
3232 0 pn2->pn_right = pn3;
3233 0 return pn2;
3234 }
3235
3236 static JSParseNode *
3237 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3238 0 {
3239 JSParseNode *pn;
3240
3241 0 pn = PropertySelector(cx, ts, tc);
3242 0 if (!pn)
3243 0 return NULL;
3244 0 if (js_MatchToken(cx, ts, TOK_DBLCOLON))
3245 0 pn = QualifiedSuffix(cx, ts, pn, tc);
3246 0 return pn;
3247 }
3248
3249 static JSParseNode *
3250 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3251 0 {
3252 JSParseNode *pn, *pn2;
3253 JSTokenType tt;
3254
3255 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
3256 0 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3257 0 if (!pn)
3258 0 return NULL;
3259 0 pn->pn_op = JSOP_TOATTRNAME;
3260 0 tt = js_GetToken(cx, ts);
3261 0 if (tt == TOK_STAR || tt == TOK_NAME) {
3262 0 pn2 = QualifiedIdentifier(cx, ts, tc);
3263 0 } else if (tt == TOK_LB) {
3264 0 pn2 = EndBracketedExpr(cx, ts, tc);
3265 } else {
3266 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3267 JSMSG_SYNTAX_ERROR);
3268 0 return NULL;
3269 }
3270 0 if (!pn2)
3271 0 return NULL;
3272 0 pn->pn_kid = pn2;
3273 0 return pn;
3274 }
3275
3276 /*
3277 * Make a TOK_LC unary node whose pn_kid is an expression.
3278 */
3279 static JSParseNode *
3280 XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc)
3281 0 {
3282 JSParseNode *pn, *pn2;
3283 uintN oldflags;
3284
3285 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
3286 0 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3287 0 if (!pn)
3288 0 return NULL;
3289
3290 /*
3291 * Turn off XML tag mode, but don't restore it after parsing this braced
3292 * expression. Instead, simply restore ts's old flags. This is required
3293 * because XMLExpr is called both from within a tag, and from within text
3294 * contained in an element, but outside of any start, end, or point tag.
3295 */
3296 0 oldflags = ts->flags;
3297 0 ts->flags = oldflags & ~TSF_XMLTAGMODE;
3298 0 pn2 = Expr(cx, ts, tc);
3299 0 if (!pn2)
3300 0 return NULL;
3301
3302 0 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
3303 0 ts->flags = oldflags;
3304 0 pn->pn_kid = pn2;
3305 0 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
3306 0 return pn;
3307 }
3308
3309 /*
3310 * Make a terminal node for oneof TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE,
3311 * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting
3312 * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole
3313 * child of a container tag.
3314 */
3315 static JSParseNode *
3316 XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3317 0 {
3318 JSParseNode *pn;
3319 JSToken *tp;
3320
3321 0 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3322 0 if (!pn)
3323 0 return NULL;
3324 0 tp = &CURRENT_TOKEN(ts);
3325 0 pn->pn_op = tp->t_op;
3326 0 pn->pn_atom = tp->t_atom;
3327 0 if (tp->type == TOK_XMLPI)
3328 0 pn->pn_atom2 = tp->t_atom2;
3329 0 return pn;
3330 }
3331
3332 /*
3333 * Parse the productions:
3334 *
3335 * XMLNameExpr:
3336 * XMLName XMLNameExpr?
3337 * { Expr } XMLNameExpr?
3338 *
3339 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces
3340 * a list of names and/or expressions, a single expression, or a single name.
3341 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type
3342 * will be TOK_LC.
3343 */
3344 static JSParseNode *
3345 XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3346 0 {
3347 JSParseNode *pn, *pn2, *list;
3348 JSTokenType tt;
3349
3350 0 pn = list = NULL;
3351 do {
3352 0 tt = CURRENT_TOKEN(ts).type;
3353 0 if (tt == TOK_LC) {
3354 0 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
3355 0 if (!pn2)
3356 0 return NULL;
3357 } else {
3358 JS_ASSERT(tt == TOK_XMLNAME);
3359 0 pn2 = XMLAtomNode(cx, ts, tc);
3360 0 if (!pn2)
3361 0 return NULL;
3362 }
3363
3364 0 if (!pn) {
3365 0 pn = pn2;
3366 } else {
3367 0 if (!list) {
3368 0 list = NewParseNode(cx, ts, PN_LIST, tc);
3369 0 if (!list)
3370 0 return NULL;
3371 0 list->pn_type = TOK_XMLNAME;
3372 0 list->pn_pos.begin = pn->pn_pos.begin;
3373 0 PN_INIT_LIST_1(list, pn);
3374 0 list->pn_extra = PNX_CANTFOLD;
3375 0 pn = list;
3376 }
3377 0 pn->pn_pos.end = pn2->pn_pos.end;
3378 0 PN_APPEND(pn, pn2);
3379 }
3380 0 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
3381
3382 0 js_UngetToken(ts);
3383 0 return pn;
3384 }
3385
3386 /*
3387 * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded
3388 * at compile time into a JSXML tree.
3389 */
3390 #define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \
3391 ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \
3392 : (pn)->pn_type != TOK_LC)
3393
3394 /*
3395 * Parse the productions:
3396 *
3397 * XMLTagContent:
3398 * XMLNameExpr
3399 * XMLTagContent S XMLNameExpr S? = S? XMLAttr
3400 * XMLTagContent S XMLNameExpr S? = S? { Expr }
3401 *
3402 * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent
3403 * produces a list of name and attribute values and/or braced expressions, a
3404 * single expression, or a single name.
3405 *
3406 * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where
3407 * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is
3408 * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and
3409 * we parsed exactly one expression.
3410 */
3411 static JSParseNode *
3412 XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3413 JSTokenType tagtype, JSAtom **namep)
3414 0 {
3415 JSParseNode *pn, *pn2, *list;
3416 JSTokenType tt;
3417
3418 0 pn = XMLNameExpr(cx, ts, tc);
3419 0 if (!pn)
3420 0 return NULL;
3421 0 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
3422 0 list = NULL;
3423
3424 0 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
3425 0 tt = js_GetToken(cx, ts);
3426 0 if (tt != TOK_XMLNAME && tt != TOK_LC) {
3427 0 js_UngetToken(ts);
3428 0 break;
3429 }
3430
3431 0 pn2 = XMLNameExpr(cx, ts, tc);
3432 0 if (!pn2)
3433 0 return NULL;
3434 0 if (!list) {
3435 0 list = NewParseNode(cx, ts, PN_LIST, tc);
3436 0 if (!list)
3437 0 return NULL;
3438 0 list->pn_type = tagtype;
3439 0 list->pn_pos.begin = pn->pn_pos.begin;
3440 0 PN_INIT_LIST_1(list, pn);
3441 0 pn = list;
3442 }
3443 0 PN_APPEND(pn, pn2);
3444 0 if (!XML_FOLDABLE(pn2))
3445 0 pn->pn_extra |= PNX_CANTFOLD;
3446
3447 0 js_MatchToken(cx, ts, TOK_XMLSPACE);
3448 0 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
3449 0 js_MatchToken(cx, ts, TOK_XMLSPACE);
3450
3451 0 tt = js_GetToken(cx, ts);
3452 0 if (tt == TOK_XMLATTR) {
3453 0 pn2 = XMLAtomNode(cx, ts, tc);
3454 0 } else if (tt == TOK_LC) {
3455 0 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
3456 0 pn->pn_extra |= PNX_CANTFOLD;
3457 } else {
3458 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3459 JSMSG_BAD_XML_ATTR_VALUE);
3460 0 return NULL;
3461 }
3462 0 if (!pn2)
3463 0 return NULL;
3464 0 pn->pn_pos.end = pn2->pn_pos.end;
3465 0 PN_APPEND(pn, pn2);
3466 }
3467
3468 0 return pn;
3469 }
3470
3471 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \
3472 JS_BEGIN_MACRO \
3473 if ((tt) <= TOK_EOF) { \
3474 if ((tt) == TOK_EOF) { \
3475 js_ReportCompileErrorNumber(cx, ts, \
3476 JSREPORT_TS | JSREPORT_ERROR, \
3477 JSMSG_END_OF_XML_SOURCE); \
3478 } \
3479 return result; \
3480 } \
3481 JS_END_MACRO
3482
3483 static JSParseNode *
3484 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3485 JSBool allowList);
3486
3487 /*
3488 * Consume XML element tag content, including the TOK_XMLETAGO (</) sequence
3489 * that opens the end tag for the container.
3490 */
3491 static JSBool
3492 XMLElementContent(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
3493 JSTreeContext *tc)
3494 0 {
3495 JSTokenType tt;
3496 JSParseNode *pn2;
3497 JSAtom *textAtom;
3498
3499 0 ts->flags &= ~TSF_XMLTAGMODE;
3500 for (;;) {
3501 0 ts->flags |= TSF_XMLTEXTMODE;
3502 0 tt = js_GetToken(cx, ts);
3503 0 ts->flags &= ~TSF_XMLTEXTMODE;
3504 0 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
3505
3506 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
3507 0 textAtom = CURRENT_TOKEN(ts).t_atom;
3508 0 if (textAtom) {
3509 /* Non-zero-length XML text scanned. */
3510 0 pn2 = XMLAtomNode(cx, ts, tc);
3511 0 if (!pn2)
3512 0 return JS_FALSE;
3513 0 pn->pn_pos.end = pn2->pn_pos.end;
3514 0 PN_APPEND(pn, pn2);
3515 }
3516
3517 0 ts->flags |= TSF_OPERAND;
3518 0 tt = js_GetToken(cx, ts);
3519 0 ts->flags &= ~TSF_OPERAND;
3520 0 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
3521 0 if (tt == TOK_XMLETAGO)
3522 0 break;
3523
3524 0 if (tt == TOK_LC) {
3525 0 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
3526 0 pn->pn_extra |= PNX_CANTFOLD;
3527 0 } else if (tt == TOK_XMLSTAGO) {
3528 0 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
3529 0 if (pn2) {
3530 0 pn2->pn_extra &= ~PNX_XMLROOT;
3531 0 pn->pn_extra |= pn2->pn_extra;
3532 }
3533 } else {
3534 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
3535 tt == TOK_XMLPI);
3536 0 pn2 = XMLAtomNode(cx, ts, tc);
3537 }
3538 0 if (!pn2)
3539 0 return JS_FALSE;
3540 0 pn->pn_pos.end = pn2->pn_pos.end;
3541 0 PN_APPEND(pn, pn2);
3542 0 }
3543
3544 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
3545 0 ts->flags |= TSF_XMLTAGMODE;
3546 0 return JS_TRUE;
3547 }
3548
3549 /*
3550 * Return a PN_LIST node containing an XML or XMLList Initialiser.
3551 */
3552 static JSParseNode *
3553 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3554 JSBool allowList)
3555 0 {
3556 JSParseNode *pn, *pn2, *list;
3557 JSBool hadSpace;
3558 JSTokenType tt;
3559 JSAtom *startAtom, *endAtom;
3560
3561 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
3562 0 pn = NewParseNode(cx, ts, PN_LIST, tc);
3563 0 if (!pn)
3564 0 return NULL;
3565
3566 0 ts->flags |= TSF_XMLTAGMODE;
3567 0 hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE);
3568 0 tt = js_GetToken(cx, ts);
3569 0 if (tt == TOK_ERROR)
3570 0 return NULL;
3571
3572 0 if (tt == TOK_XMLNAME || tt == TOK_LC) {
3573 /*
3574 * XMLElement. Append the tag and its contents, if any, to pn.
3575 */
3576 0 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
3577 0 if (!pn2)
3578 0 return NULL;
3579 0 js_MatchToken(cx, ts, TOK_XMLSPACE);
3580
3581 0 tt = js_GetToken(cx, ts);
3582 0 if (tt == TOK_XMLPTAGC) {
3583 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
3584 0 if (pn2->pn_type == TOK_XMLSTAGO) {
3585 0 PN_INIT_LIST(pn);
3586 0 RecycleTree(pn, tc);
3587 0 pn = pn2;
3588 } else {
3589 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
3590 pn2->pn_type == TOK_LC);
3591 0 PN_INIT_LIST_1(pn, pn2);
3592 0 if (!XML_FOLDABLE(pn2))
3593 0 pn->pn_extra |= PNX_CANTFOLD;
3594 }
3595 0 pn->pn_type = TOK_XMLPTAGC;
3596 0 pn->pn_extra |= PNX_XMLROOT;
3597 } else {
3598 /* We had better have a tag-close (>) at this point. */
3599 0 if (tt != TOK_XMLTAGC) {
3600 0 js_ReportCompileErrorNumber(cx, ts,
3601 JSREPORT_TS | JSREPORT_ERROR,
3602 JSMSG_BAD_XML_TAG_SYNTAX);
3603 0 return NULL;
3604 }
3605 0 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3606
3607 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
3608 0 if (pn2->pn_type != TOK_XMLSTAGO) {
3609 0 PN_INIT_LIST_1(pn, pn2);
3610 0 if (!XML_FOLDABLE(pn2))
3611 0 pn->pn_extra |= PNX_CANTFOLD;
3612 0 pn2 = pn;
3613 0 pn = NewParseNode(cx, ts, PN_LIST, tc);
3614 0 if (!pn)
3615 0 return NULL;
3616 }
3617
3618 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
3619 0 pn->pn_type = TOK_XMLELEM;
3620 0 PN_INIT_LIST_1(pn, pn2);
3621 0 if (!XML_FOLDABLE(pn2))
3622 0 pn->pn_extra |= PNX_CANTFOLD;
3623 0 pn->pn_extra |= PNX_XMLROOT;
3624
3625 /* Get element contents and delimiting end-tag-open sequence. */
3626 0 if (!XMLElementContent(cx, ts, pn, tc))
3627 0 return NULL;
3628
3629 0 js_MatchToken(cx, ts, TOK_XMLSPACE);
3630 0 tt = js_GetToken(cx, ts);
3631 0 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
3632 0 if (tt != TOK_XMLNAME && tt != TOK_LC) {
3633 0 js_ReportCompileErrorNumber(cx, ts,
3634 JSREPORT_TS | JSREPORT_ERROR,
3635 JSMSG_BAD_XML_TAG_SYNTAX);
3636 0 return NULL;
3637 }
3638
3639 /* Parse end tag; check mismatch at compile-time if we can. */
3640 0 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
3641 0 if (!pn2)
3642 0 return NULL;
3643 0 if (pn2->pn_type == TOK_XMLETAGO) {
3644 /* Oops, end tag has attributes! */
3645 0 js_ReportCompileErrorNumber(cx, ts,
3646 JSREPORT_TS | JSREPORT_ERROR,
3647 JSMSG_BAD_XML_TAG_SYNTAX);
3648 0 return NULL;
3649 }
3650 0 if (endAtom && startAtom && endAtom != startAtom) {
3651 /* End vs. start tag name mismatch: point to the tag name. */
3652 0 ++pn2->pn_pos.begin.index;
3653 0 js_ReportCompileErrorNumber(cx, pn2,
3654 JSREPORT_PN | JSREPORT_ERROR,
3655 JSMSG_XML_TAG_NAME_MISMATCH);
3656 0 return NULL;
3657 }
3658
3659 /* Make a TOK_XMLETAGO list with pn2 as its single child. */
3660 JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC);
3661 0 list = NewParseNode(cx, ts, PN_LIST, tc);
3662 0 if (!list)
3663 0 return NULL;
3664 0 list->pn_type = TOK_XMLETAGO;
3665 0 PN_INIT_LIST_1(list, pn2);
3666 0 PN_APPEND(pn, list);
3667 0 if (!XML_FOLDABLE(pn2)) {
3668 0 list->pn_extra |= PNX_CANTFOLD;
3669 0 pn->pn_extra |= PNX_CANTFOLD;
3670 }
3671
3672 0 js_MatchToken(cx, ts, TOK_XMLSPACE);
3673 0 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
3674 }
3675
3676 /* Set pn_op now that pn has been updated to its final value. */
3677 0 pn->pn_op = JSOP_TOXML;
3678 0 } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) {
3679 /* XMLList Initialiser. */
3680 0 pn->pn_type = TOK_XMLLIST;
3681 0 pn->pn_op = JSOP_TOXMLLIST;
3682 0 PN_INIT_LIST(pn);
3683 0 pn->pn_extra |= PNX_XMLROOT;
3684 0 if (!XMLElementContent(cx, ts, pn, tc))
3685 0 return NULL;
3686
3687 0 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
3688 } else {
3689 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3690 JSMSG_BAD_XML_NAME_SYNTAX);
3691 0 return NULL;
3692 }
3693
3694 0 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3695 0 ts->flags &= ~TSF_XMLTAGMODE;
3696 0 return pn;
3697 }
3698
3699 static JSParseNode *
3700 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3701 JSBool allowList)
3702 0 {
3703 uint32 oldopts;
3704 JSParseNode *pn;
3705
3706 /*
3707 * Force XML support to be enabled so that comments and CDATA literals
3708 * are recognized, instead of <! followed by -- starting an HTML comment
3709 * to end of line (used in script tags to hide content from old browsers
3710 * that don't recognize <script>).
3711 */
3712 0 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
3713 0 pn = XMLElementOrList(cx, ts, tc, allowList);
3714 0 JS_SetOptions(cx, oldopts);
3715 0 return pn;
3716 }
3717
3718 JS_FRIEND_API(JSParseNode *)
3719 js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
3720 JSBool allowList)
3721 0 {
3722 JSStackFrame *fp, frame;
3723 JSParseNode *pn;
3724 JSTreeContext tc;
3725 JSTokenType tt;
3726
3727 /*
3728 * Push a compiler frame if we have no frames, or if the top frame is a
3729 * lightweight function activation, or if its scope chain doesn't match
3730 * the one passed to us.
3731 */
3732 0 fp = cx->fp;
3733 0 if (!fp || !fp->varobj || fp->scopeChain != chain) {
3734 0 memset(&frame, 0, sizeof frame);
3735 0 frame.varobj = frame.scopeChain = chain;
3736 0 if (cx->options & JSOPTION_VAROBJFIX) {
3737 0 while ((chain = JS_GetParent(cx, chain)) != NULL)
3738 0 frame.varobj = chain;
3739 }
3740 0 frame.down = fp;
3741 0 if (fp) {
3742 0 frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
3743 JSFRAME_SCRIPT_OBJECT);
3744 }
3745 0 cx->fp = &frame;
3746 }
3747
3748 0 JS_KEEP_ATOMS(cx->runtime);
3749 0 TREE_CONTEXT_INIT(&tc);
3750
3751 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
3752 0 ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
3753 0 tt = js_GetToken(cx, ts);
3754 0 ts->flags &= ~TSF_OPERAND;
3755
3756 0 if (tt != TOK_XMLSTAGO) {
3757 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3758 JSMSG_BAD_XML_MARKUP);
3759 0 pn = NULL;
3760 } else {
3761 0 pn = XMLElementOrListRoot(cx, ts, &tc, allowList);
3762 }
3763
3764 0 ts->flags &= ~TSF_XMLONLYMODE;
3765 TREE_CONTEXT_FINISH(&tc);
3766 0 JS_UNKEEP_ATOMS(cx->runtime);
3767 0 cx->fp = fp;
3768 0 return pn;
3769 }
3770
3771 #endif /* JS_HAS_XMLSUPPORT */
3772
3773 static JSParseNode *
3774 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3775 1331185 {
3776 JSTokenType tt;
3777 JSParseNode *pn, *pn2, *pn3;
3778 #if JS_HAS_GETTER_SETTER
3779 JSAtom *atom;
3780 JSRuntime *rt;
3781 #endif
3782
3783 #if JS_HAS_SHARP_VARS
3784 JSParseNode *defsharp;
3785 JSBool notsharp;
3786
3787 1331185 defsharp = NULL;
3788 1331185 notsharp = JS_FALSE;
3789 1331185 again:
3790 /*
3791 * Control flows here after #n= is scanned. If the following primary is
3792 * not valid after such a "sharp variable" definition, the tt switch case
3793 * should set notsharp.
3794 */
3795 #endif
3796
3797 1331185 CHECK_RECURSION();
3798
3799 1331185 ts->flags |= TSF_OPERAND;
3800 1331185 tt = js_GetToken(cx, ts);
3801 1331185 ts->flags &= ~TSF_OPERAND;
3802
3803 #if JS_HAS_GETTER_SETTER
3804 1331185 if (tt == TOK_NAME) {
3805 1008578 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
3806 1008578 if (tt == TOK_ERROR)
3807 0 return NULL;
3808 }
3809 #endif
3810
3811 1331185 switch (tt) {
3812 #if JS_HAS_LEXICAL_CLOSURE || JS_HAS_XML_SUPPORT
3813 case TOK_FUNCTION:
3814 #if JS_HAS_XML_SUPPORT
3815 54 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
3816 0 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
3817 0 if (!pn2)
3818 0 return NULL;
3819 0 pn2->pn_type = TOK_FUNCTION;
3820 0 pn = QualifiedSuffix(cx, ts, pn2, tc);
3821 0 if (!pn)
3822 0 return NULL;
3823 0 break;
3824 }
3825 #endif
3826 54 pn = FunctionExpr(cx, ts, tc);
3827 54 if (!pn)
3828 0 return NULL;
3829 54 break;
3830 #endif
3831
3832 #if JS_HAS_INITIALIZERS
3833 case TOK_LB:
3834 {
3835 JSBool matched;
3836 jsuint atomIndex;
3837
3838 8021 pn = NewParseNode(cx, ts, PN_LIST, tc);
3839 8021 if (!pn)
3840 0 return NULL;
3841 8021 pn->pn_type = TOK_RB;
3842
3843 #if JS_HAS_SHARP_VARS
3844 8021 if (defsharp) {
3845 0 PN_INIT_LIST_1(pn, defsharp);
3846 0 defsharp = NULL;
3847 } else
3848 #endif
3849 8021 PN_INIT_LIST(pn);
3850
3851 8021 ts->flags |= TSF_OPERAND;
3852 8021 matched = js_MatchToken(cx, ts, TOK_RB);
3853 8021 ts->flags &= ~TSF_OPERAND;
3854 8021 if (!matched) {
3855 35167 for (atomIndex = 0; ; atomIndex++) {
3856 35167 if (atomIndex == ATOM_INDEX_LIMIT) {
3857 0 js_ReportCompileErrorNumber(cx, ts,
3858 JSREPORT_TS | JSREPORT_ERROR,
3859 JSMSG_ARRAY_INIT_TOO_BIG);
3860 0 return NULL;
3861 }
3862
3863 35167 ts->flags |= TSF_OPERAND;
3864 35167 tt = js_PeekToken(cx, ts);
3865 35167 ts->flags &= ~TSF_OPERAND;
3866 35167 if (tt == TOK_RB) {
3867 0 pn->pn_extra |= PNX_ENDCOMMA;
3868 0 break;
3869 }
3870
3871 35167 if (tt == TOK_COMMA) {
3872 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
3873 0 js_MatchToken(cx, ts, TOK_COMMA);
3874 0 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
3875 } else {
3876 35167 pn2 = AssignExpr(cx, ts, tc);
3877 }
3878 35167 if (!pn2)
3879 0 return NULL;
3880 35167 PN_APPEND(pn, pn2);
3881
3882 35167 if (tt != TOK_COMMA) {
3883 /* If we didn't already match TOK_COMMA in above case. */
3884 35167 if (!js_MatchToken(cx, ts, TOK_COMMA))
3885 7956 break;
3886 }
3887 27211 }
3888
3889 7956 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
3890 }
3891 8021 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3892 8021 return pn;
3893 }
3894
3895 case TOK_LC:
3896 0 pn = NewParseNode(cx, ts, PN_LIST, tc);
3897 0 if (!pn)
3898 0 return NULL;
3899 0 pn->pn_type = TOK_RC;
3900
3901 #if JS_HAS_SHARP_VARS
3902 0 if (defsharp) {
3903 0 PN_INIT_LIST_1(pn, defsharp);
3904 0 defsharp = NULL;
3905 } else
3906 #endif
3907 0 PN_INIT_LIST(pn);
3908
3909 0 if (!js_MatchToken(cx, ts, TOK_RC)) {
3910 do {
3911 JSOp op;
3912
3913 0 tt = js_GetToken(cx, ts);
3914 0 switch (tt) {
3915 case TOK_NUMBER:
3916 0 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
3917 0 if (pn3)
3918 0 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
3919 0 break;
3920 case TOK_NAME:
3921 #if JS_HAS_GETTER_SETTER
3922 0 atom = CURRENT_TOKEN(ts).t_atom;
3923 0 rt = cx->runtime;
3924 0 if (atom == rt->atomState.getAtom ||
3925 atom == rt->atomState.setAtom) {
3926 0 op = (atom == rt->atomState.getAtom)
3927 ? JSOP_GETTER
3928 : JSOP_SETTER;
3929 0 if (js_MatchToken(cx, ts, TOK_NAME)) {
3930 0 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3931 0 if (!pn3)
3932 0 return NULL;
3933 0 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3934 0 pn3->pn_expr = NULL;
3935
3936 /* We have to fake a 'function' token here. */
3937 0 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
3938 0 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
3939 0 pn2 = FunctionExpr(cx, ts, tc);
3940 0 pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
3941 0 goto skip;
3942 }
3943 }
3944 /* else fall thru ... */
3945 #endif
3946 case TOK_STRING:
3947 0 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
3948 0 if (pn3)
3949 0 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3950 0 break;
3951 case TOK_RC:
3952 0 if (!js_ReportCompileErrorNumber(cx, ts,
3953 JSREPORT_TS |
3954 JSREPORT_WARNING |
3955 JSREPORT_STRICT,
3956 JSMSG_TRAILING_COMMA)) {
3957 0 return NULL;
3958 }
3959 0 goto end_obj_init;
3960 default:
3961 0 js_ReportCompileErrorNumber(cx, ts,
3962 JSREPORT_TS | JSREPORT_ERROR,
3963 JSMSG_BAD_PROP_ID);
3964 0 return NULL;
3965 }
3966
3967 0 tt = js_GetToken(cx, ts);
3968 #if JS_HAS_GETTER_SETTER
3969 0 if (tt == TOK_NAME) {
3970 0 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
3971 0 if (tt == TOK_ERROR)
3972 0 return NULL;
3973 }
3974 #endif
3975 0 if (tt != TOK_COLON) {
3976 0 js_ReportCompileErrorNumber(cx, ts,
3977 JSREPORT_TS | JSREPORT_ERROR,
3978 JSMSG_COLON_AFTER_ID);
3979 0 return NULL;
3980 }
3981 0 op = CURRENT_TOKEN(ts).t_op;
3982 0 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
3983 tc);
3984 #if JS_HAS_GETTER_SETTER
3985 0 skip:
3986 #endif
3987 0 if (!pn2)
3988 0 return NULL;
3989 0 PN_APPEND(pn, pn2);
3990 0 } while (js_MatchToken(cx, ts, TOK_COMMA));
3991
3992 0 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
3993 }
3994 0 end_obj_init:
3995 0 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3996 0 return pn;
3997
3998 #if JS_HAS_SHARP_VARS
3999 case TOK_DEFSHARP:
4000 0 if (defsharp)
4001 0 goto badsharp;
4002 0 defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
4003 0 if (!defsharp)
4004 0 return NULL;
4005 0 defsharp->pn_kid = NULL;
4006 0 defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
4007 0 goto again;
4008
4009 case TOK_USESHARP:
4010 /* Check for forward/dangling references at runtime, to allow eval. */
4011 0 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4012 0 if (!pn)
4013 0 return NULL;
4014 0 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
4015 0 notsharp = JS_TRUE;
4016 0 break;
4017 #endif /* JS_HAS_SHARP_VARS */
4018 #endif /* JS_HAS_INITIALIZERS */
4019
4020 case TOK_LP:
4021 1931 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4022 1931 if (!pn)
4023 0 return NULL;
4024 1931 pn2 = BracketedExpr(cx, ts, tc);
4025 1931 if (!pn2)
4026 0 return NULL;
4027
4028 1931 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
4029 1931 pn->pn_type = TOK_RP;
4030 1931 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4031 1931 pn->pn_kid = pn2;
4032 1931 break;
4033
4034 #if JS_HAS_XML_SUPPORT
4035 case TOK_STAR:
4036 0 pn = QualifiedIdentifier(cx, ts, tc);
4037 0 if (!pn)
4038 0 return NULL;
4039 0 notsharp = JS_TRUE;
4040 0 break;
4041
4042 case TOK_AT:
4043 0 pn = AttributeIdentifier(cx, ts, tc);
4044 0 if (!pn)
4045 0 return NULL;
4046 0 notsharp = JS_TRUE;
4047 0 break;
4048
4049 case TOK_XMLSTAGO:
4050 0 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
4051 0 if (!pn)
4052 0 return NULL;
4053 0 notsharp = JS_TRUE; /* XXXbe could be sharp? */
4054 0 break;
4055 #endif /* JS_HAS_XML_SUPPORT */
4056
4057 case TOK_STRING:
4058 #if JS_HAS_SHARP_VARS
4059 272568 notsharp = JS_TRUE;
4060 /* FALL THROUGH */
4061 #endif
4062
4063 #if JS_HAS_XML_SUPPORT
4064 case TOK_XMLCDATA:
4065 case TOK_XMLCOMMENT:
4066 case TOK_XMLPI:
4067 #endif
4068 case TOK_NAME:
4069 case TOK_OBJECT:
4070 1281941 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4071 1281941 if (!pn)
4072 0 return NULL;
4073 1281941 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
4074 #if JS_HAS_XML_SUPPORT
4075 1281941 if (tt == TOK_XMLPI)
4076 0 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
4077 else
4078 #endif
4079 1281941 pn->pn_op = CURRENT_TOKEN(ts).t_op;
4080 1281941 if (tt == TOK_NAME) {
4081 1008578 pn->pn_arity = PN_NAME;
4082 1008578 pn->pn_expr = NULL;
4083 1008578 pn->pn_slot = -1;
4084 1008578 pn->pn_attrs = 0;
4085
4086 #if JS_HAS_XML_SUPPORT
4087 1008578 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
4088 0 pn = QualifiedSuffix(cx, ts, pn, tc);
4089 0 if (!pn)
4090 0 return NULL;
4091 0 break;
4092 }
4093 #endif
4094
4095 /* Unqualified __parent__ and __proto__ uses require activations. */
4096 1008578 if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
4097 pn->pn_atom == cx->runtime->atomState.protoAtom) {
4098 0 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4099 } else {
4100 JSAtomListElement *ale;
4101 JSStackFrame *fp;
4102 JSStmtInfo *stmt;
4103
4104 /* Measure optimizable global variable uses. */
4105 1008578 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
4106 1008578 if (ale &&
4107 !(fp = cx->fp)->fun &&
4108 fp->scopeChain == fp->varobj &&
4109 !js_InWithStatement(tc) &&
4110 !js_InCatchBlock(tc, pn->pn_atom)) {
4111 2249 tc->globalUses++;
4112 8312 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
4113 8232 if (STMT_IS_LOOP(stmt)) {
4114 2169 tc->loopyGlobalUses++;
4115 2169 break;
4116 }
4117 }
4118 }
4119 }
4120 }
4121 1281941 break;
4122
4123 case TOK_NUMBER:
4124 33561 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4125 33561 if (!pn)
4126 0 return NULL;
4127 33561 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
4128 #if JS_HAS_SHARP_VARS
4129 33561 notsharp = JS_TRUE;
4130 #endif
4131 33561 break;
4132
4133 case TOK_PRIMARY:
4134 5677 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4135 5677 if (!pn)
4136 0 return NULL;
4137 5677 pn->pn_op = CURRENT_TOKEN(ts).t_op;
4138 #if JS_HAS_SHARP_VARS
4139 5677 notsharp = JS_TRUE;
4140 #endif
4141 5677 break;
4142
4143 #if !JS_HAS_EXPORT_IMPORT
4144 case TOK_EXPORT:
4145 case TOK_IMPORT:
4146 #endif
4147 case TOK_ERROR:
4148 /* The scanner or one of its subroutines reported the error. */
4149 0 return NULL;
4150
4151 default:
4152 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4153 JSMSG_SYNTAX_ERROR);
4154 0 return NULL;
4155 }
4156
4157 #if JS_HAS_SHARP_VARS
4158 1323164 if (defsharp) {
4159 0 if (notsharp) {
4160 0 badsharp:
4161 0 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4162 JSMSG_BAD_SHARP_VAR_DEF);
4163 0 return NULL;
4164 }
4165 0 defsharp->pn_kid = pn;
4166 0 return defsharp;
4167 }
4168 #endif
4169 1323164 return pn;
4170 }
4171
4172 static JSBool
4173 ContainsVarStmt(JSParseNode *pn)
4174 1113994 {
4175 JSParseNode *pn2;
4176
4177 1113994 if (!pn)
4178 28723 return JS_FALSE;
4179 1085271 switch (pn->pn_arity) {
4180 case PN_LIST:
4181 274755 if (pn->pn_type == TOK_VAR)
4182 4804 return JS_TRUE;
4183 977859 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4184 713697 if (ContainsVarStmt(pn2))
4185 5789 return JS_TRUE;
4186 }
4187 264162 break;
4188 case PN_TERNARY:
4189 24648 return ContainsVarStmt(pn->pn_kid1) ||
4190 ContainsVarStmt(pn->pn_kid2) ||
4191 ContainsVarStmt(pn->pn_kid3);
4192 case PN_BINARY:
4193 /*
4194 * Limit recursion if pn is a binary expression, which can't contain a
4195 * var statement.
4196 */
4197 71557 if (pn->pn_op != JSOP_NOP)
4198 45084 return JS_FALSE;
4199 26473 return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
4200 case PN_UNARY:
4201 212117 if (pn->pn_op != JSOP_NOP)
4202 792 return JS_FALSE;
4203 211325 return ContainsVarStmt(pn->pn_kid);
4204 default:;
4205 }
4206 766356 return JS_FALSE;
4207 }
4208
4209 /*
4210 * Fold from one constant type to another.
4211 * XXX handles only strings and numbers for now
4212 */
4213 static JSBool
4214 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
4215 20295 {
4216 20295 if (pn->pn_type != type) {
4217 19541 switch (type) {
4218 case TOK_NUMBER:
4219 12747 if (pn->pn_type == TOK_STRING) {
4220 jsdouble d;
4221 0 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
4222 0 return JS_FALSE;
4223 0 pn->pn_dval = d;
4224 0 pn->pn_type = TOK_NUMBER;
4225 0 pn->pn_op = JSOP_NUMBER;
4226 }
4227 12747 break;
4228
4229 case TOK_STRING:
4230 6794 if (pn->pn_type == TOK_NUMBER) {
4231 0 JSString *str = js_NumberToString(cx, pn->pn_dval);
4232 0 if (!str)
4233 0 return JS_FALSE;
4234 0 pn->pn_atom = js_AtomizeString(cx, str, 0);
4235 0 if (!pn->pn_atom)
4236 0 return JS_FALSE;
4237 0 pn->pn_type = TOK_STRING;
4238 0 pn->pn_op = JSOP_STRING;
4239 }
4240 break;
4241
4242 default:;
4243 }
4244 }
4245 20295 return JS_TRUE;
4246 }
4247
4248 /*
4249 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
4250 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
4251 * a successful call to this function.
4252 */
4253 static JSBool
4254 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
4255 JSParseNode *pn, JSTreeContext *tc)
4256 0 {
4257 jsdouble d, d2;
4258 int32 i, j;
4259 uint32 u;
4260
4261 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
4262 0 d = pn1->pn_dval;
4263 0 d2 = pn2->pn_dval;
4264 0 switch (op) {
4265 case JSOP_LSH:
4266 case JSOP_RSH:
4267 0 if (!js_DoubleToECMAInt32(cx, d, &i))
4268 0 return JS_FALSE;
4269 0 if (!js_DoubleToECMAInt32(cx, d2, &j))
4270 0 return JS_FALSE;
4271 0 j &= 31;
4272 0 d = (op == JSOP_LSH) ? i << j : i >> j;
4273 0 break;
4274
4275 case JSOP_URSH:
4276 0 if (!js_DoubleToECMAUint32(cx, d, &u))
4277 0 return JS_FALSE;
4278 0 if (!js_DoubleToECMAInt32(cx, d2, &j))
4279 0 return JS_FALSE;
4280 0 j &= 31;
4281 0 d = u >> j;
4282 0 break;
4283
4284 case JSOP_ADD:
4285 0 d += d2;
4286 0 break;
4287
4288 case JSOP_SUB:
4289 0 d -= d2;
4290 0 break;
4291
4292 case JSOP_MUL:
4293 0 d *= d2;
4294 0 break;
4295
4296 case JSOP_DIV:
4297 0 if (d2 == 0) {
4298 #if defined(XP_WIN)
4299 /* XXX MSVC miscompiles such that (NaN == 0) */
4300 if (JSDOUBLE_IS_NaN(d2))
4301 d = *cx->runtime->jsNaN;
4302 else
4303 #endif
4304 0 if (d == 0 || JSDOUBLE_IS_NaN(d))
4305 0 d = *cx->runtime->jsNaN;
4306 0 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
4307 0 d = *cx->runtime->jsNegativeInfinity;
4308 else
4309 0 d = *cx->runtime->jsPositiveInfinity;
4310 } else {
4311 0 d /= d2;
4312 }
4313 0 break;
4314
4315 case JSOP_MOD:
4316 0 if (d2 == 0) {
4317 0 d = *cx->runtime->jsNaN;
4318 } else {
4319 #if defined(XP_WIN)
4320 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
4321 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
4322 #endif
4323 0 d = fmod(d, d2);
4324 }
4325 break;
4326
4327 default:;
4328 }
4329
4330 /* Take care to allow pn1 or pn2 to alias pn. */
4331 0 if (pn1 != pn)
4332 0 RecycleTree(pn1, tc);
4333 0 if (pn2 != pn)
4334 0 RecycleTree(pn2, tc);
4335 0 pn->pn_type = TOK_NUMBER;
4336 0 pn->pn_op = JSOP_NUMBER;
4337 0 pn->pn_arity = PN_NULLARY;
4338 0 pn->pn_dval = d;
4339 0 return JS_TRUE;
4340 }
4341
4342 #if JS_HAS_XML_SUPPORT
4343
4344 static JSBool
4345 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4346 0 {
4347 JSTokenType tt;
4348 JSParseNode **pnp, *pn1, *pn2;
4349 JSString *accum, *str;
4350 uint32 i, j;
4351
4352 JS_ASSERT(pn->pn_arity == PN_LIST);
4353 0 tt = pn->pn_type;
4354 0 pnp = &pn->pn_head;
4355 0 pn1 = *pnp;
4356 0 accum = NULL;
4357 0 if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
4358 0 if (tt == TOK_XMLETAGO)
4359 0 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
4360 0 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
4361 0 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
4362 }
4363
4364 0 for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
4365 /* The parser already rejected end-tags with attributes. */
4366 JS_ASSERT(tt != TOK_XMLETAGO || i == 0);
4367 0 switch (pn2->pn_type) {
4368 case TOK_XMLATTR:
4369 0 if (!accum)
4370 0 goto cantfold;
4371 /* FALL THROUGH */
4372 case TOK_XMLNAME:
4373 case TOK_XMLSPACE:
4374 case TOK_XMLTEXT:
4375 case TOK_STRING:
4376 0 if (pn2->pn_arity == PN_LIST)
4377 0 goto cantfold;
4378 0 str = ATOM_TO_STRING(pn2->pn_atom);
4379 0 break;
4380
4381 case TOK_XMLCDATA:
4382 0 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
4383 0 if (!str)
4384 0 return JS_FALSE;
4385 0 break;
4386
4387 case TOK_XMLCOMMENT:
4388 0 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
4389 0 if (!str)
4390 0 return JS_FALSE;
4391 0 break;
4392
4393 case TOK_XMLPI:
4394 0 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
4395 ATOM_TO_STRING(pn2->pn_atom2));
4396 0 if (!str)
4397 0 return JS_FALSE;
4398 0 break;
4399
4400 0 cantfold:
4401 default:
4402 JS_ASSERT(*pnp == pn1);
4403 0 if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) &&
4404 (i & 1) ^ (j & 1)) {
4405 #ifdef DEBUG_brendanXXX
4406 printf("1: %d, %d => %s\n",
4407 i, j, accum ? JS_GetStringBytes(accum) : "NULL");
4408 #endif
4409 0 } else if (accum && pn1 != pn2) {
4410 0 while (pn1->pn_next != pn2) {
4411 0 pn1 = RecycleTree(pn1, tc);
4412 0 --pn->pn_count;
4413 }
4414 0 pn1->pn_type = TOK_XMLTEXT;
4415 0 pn1->pn_op = JSOP_STRING;
4416 0 pn1->pn_arity = PN_NULLARY;
4417 0 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
4418 0 if (!pn1->pn_atom)
4419 0 return JS_FALSE;
4420 JS_ASSERT(pnp != &pn1->pn_next);
4421 0 *pnp = pn1;
4422 }
4423 0 pnp = &pn2->pn_next;
4424 0 pn1 = *pnp;
4425 0 accum = NULL;
4426 0 continue;
4427 }
4428
4429 0 if (accum) {
4430 0 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
4431 ? js_AddAttributePart(cx, i & 1, accum, str)
4432 : js_ConcatStrings(cx, accum, str);
4433 0 if (!str)
4434 0 return JS_FALSE;
4435 #ifdef DEBUG_brendanXXX
4436 printf("2: %d, %d => %s (%u)\n",
4437 i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str));
4438 #endif
4439 0 ++j;
4440 }
4441 0 accum = str;
4442 }
4443
4444 0 if (accum) {
4445 0 str = NULL;
4446 0 if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
4447 0 if (tt == TOK_XMLPTAGC)
4448 0 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
4449 0 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
4450 0 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
4451 }
4452 0 if (str) {
4453 0 accum = js_ConcatStrings(cx, accum, str);
4454 0 if (!accum)
4455 0 return JS_FALSE;
4456 }
4457
4458 JS_ASSERT(*pnp == pn1);
4459 0 while (pn1->pn_next) {
4460 0 pn1 = RecycleTree(pn1, tc);
4461 0 --pn->pn_count;
4462 }
4463 0 pn1->pn_type = TOK_XMLTEXT;
4464 0 pn1->pn_op = JSOP_STRING;
4465 0 pn1->pn_arity = PN_NULLARY;
4466 0 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
4467 0 if (!pn1->pn_atom)
4468 0 return JS_FALSE;
4469 JS_ASSERT(pnp != &pn1->pn_next);
4470 0 *pnp = pn1;
4471 }
4472
4473 0 if (pn1 && pn->pn_count == 1) {
4474 /*
4475 * Only one node under pn, and it has been folded: move pn1 onto pn
4476 * unless pn is an XML root (in which case we need it to tell the code
4477 * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an
4478 * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid
4479 * extra "<" and "/>" bracketing at runtime.
4480 */
4481 0 if (!(pn->pn_extra & PNX_XMLROOT)) {
4482 0 PN_MOVE_NODE(pn, pn1);
4483 0 } else if (tt == TOK_XMLPTAGC) {
4484 0 pn->pn_type = TOK_XMLELEM;
4485 0 pn->pn_op = JSOP_TOXML;
4486 }
4487 }
4488 0 return JS_TRUE;
4489 }
4490
4491 #endif /* JS_HAS_XML_SUPPORT */
4492
4493 JSBool
4494 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4495 2520408 {
4496 2520408 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
4497 int stackDummy;
4498
4499 2520408 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4500 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4501 0 return JS_FALSE;
4502 }
4503
4504 2520408 switch (pn->pn_arity) {
4505 case PN_FUNC:
4506 {
4507 7713 uint16 oldflags = tc->flags;
4508
4509 7713 tc->flags = (uint16) pn->pn_flags;
4510 7713 if (!js_FoldConstants(cx, pn->pn_body, tc))
4511 0 return JS_FALSE;
4512 7713 tc->flags = oldflags;
4513 7713 break;
4514 }
4515
4516 case PN_LIST:
4517 #if 0 /* JS_HAS_XML_SUPPORT */
4518 switch (pn->pn_type) {
4519 case TOK_XMLELEM:
4520 case TOK_XMLLIST:
4521 case TOK_XMLPTAGC:
4522 /*
4523 * Try to fold this XML parse tree once, from the top down, into
4524 * a JSXML tree with just one object wrapping the tree root.
4525 *
4526 * Certain subtrees could be folded similarly, but we'd have to
4527 * ensure that none used namespace prefixes declared elsewhere in
4528 * its super-tree, and we would have to convert each XML object
4529 * created at runtime for such sub-trees back into a string, and
4530 * concatenate and re-parse anyway.
4531 */
4532 if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT &&
4533 !(tc->flags & TCF_HAS_DEFXMLNS)) {
4534 JSObject *obj;
4535 JSAtom *atom;
4536
4537 obj = js_ParseNodeToXMLObject(cx, pn);
4538 if (!obj)
4539 return JS_FALSE;
4540 atom = js_AtomizeObject(cx, obj, 0);
4541 if (!atom)
4542 return JS_FALSE;
4543 pn->pn_op = JSOP_XMLOBJECT;
4544 pn->pn_arity = PN_NULLARY;
4545 pn->pn_atom = atom;
4546 return JS_TRUE;
4547 }
4548
4549 /*
4550 * Can't fold from parse node to XML tree -- try folding strings
4551 * as much as possible, and folding XML sub-trees bottom up to
4552 * minimize string concatenation and ToXML/ToXMLList operations
4553 * at runtime.
4554 */
4555 break;
4556
4557 default:;
4558 }
4559 #endif
4560
4561 /* Save the list head in pn1 for later use. */
4562 1885255 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4563 1310064 if (!js_FoldConstants(cx, pn2, tc))
4564 0 return JS_FALSE;
4565 }
4566 575191 break;
4567
4568 case PN_TERNARY:
4569 /* Any kid may be null (e.g. for (;;)). */
4570 35499 pn1 = pn->pn_kid1;
4571 35499 pn2 = pn->pn_kid2;
4572 35499 pn3 = pn->pn_kid3;
4573 35499 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4574 0 return JS_FALSE;
4575 35499 if (pn2 && !js_FoldConstants(cx, pn2, tc))
4576 0 return JS_FALSE;
4577 35499 if (pn3 && !js_FoldConstants(cx, pn3, tc))
4578 0 return JS_FALSE;
4579 35499 break;
4580
4581 case PN_BINARY:
4582 /* First kid may be null (for default case in switch). */
4583 148511 pn1 = pn->pn_left;
4584 148511 pn2 = pn->pn_right;
4585 148511 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4586 0 return JS_FALSE;
4587 148511 if (!js_FoldConstants(cx, pn2, tc))
4588 0 return JS_FALSE;
4589 148511 break;
4590
4591 case PN_UNARY:
4592 /* Our kid may be null (e.g. return; vs. return e;). */
4593 489242 pn1 = pn->pn_kid;
4594 489242 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4595 0 return JS_FALSE;
4596 489242 break;
4597
4598 case PN_NAME:
4599 /*
4600 * Skip pn1 down along a chain of dotted member expressions to avoid
4601 * excessive recursion. Our only goal here is to fold constants (if
4602 * any) in the primary expression operand to the left of the first
4603 * dot in the chain.
4604 */
4605 951651 pn1 = pn->pn_expr;
4606 1989727 while (pn1 && pn1->pn_arity == PN_NAME)
4607 86425 pn1 = pn1->pn_expr;
4608 951651 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4609 0 return JS_FALSE;
4610 break;
4611
4612 case PN_NULLARY:
4613 break;
4614 }
4615
4616 2520408 switch (pn->pn_type) {
4617 case TOK_IF:
4618 34237 if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
4619 break;
4620 /* FALL THROUGH */
4621
4622 case TOK_HOOK:
4623 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
4624 30431 switch (pn1->pn_type) {
4625 case TOK_NUMBER:
4626 0 if (pn1->pn_dval == 0)
4627 0 pn2 = pn3;
4628 0 break;
4629 case TOK_STRING:
4630 0 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
4631 0 pn2 = pn3;
4632 0 break;
4633 case TOK_PRIMARY:
4634 0 if (pn1->pn_op == JSOP_TRUE)
4635 0 break;
4636 0 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
4637 0 pn2 = pn3;
4638 0 break;
4639 }
4640 /* FALL THROUGH */
4641 default:
4642 /* Early return to dodge common code that copies pn2 to pn. */
4643 30431 return JS_TRUE;
4644 }
4645
4646 0 if (pn2) {
4647 /* pn2 is the then- or else-statement subtree to compile. */
4648 0 PN_MOVE_NODE(pn, pn2);
4649 } else {
4650 /* False condition and no else: make pn an empty statement. */
4651 0 pn->pn_type = TOK_SEMI;
4652 0 pn->pn_arity = PN_UNARY;
4653 0 pn->pn_kid = NULL;
4654 }
4655 0 RecycleTree(pn2, tc);
4656 0 if (pn3 && pn3 != pn2)
4657 0 RecycleTree(pn3, tc);
4658 0 break;
4659
4660 case TOK_PLUS:
4661 17568 if (pn->pn_arity == PN_LIST) {
4662 size_t length, length2;
4663 jschar *chars;
4664 JSString *str, *str2;
4665
4666 /*
4667 * Any string literal term with all others number or string means
4668 * this is a concatenation. If any term is not a string or number
4669 * literal, we can't fold.
4670 */
4671 JS_ASSERT(pn->pn_count > 2);
4672 4440 if (pn->pn_extra & PNX_CANTFOLD)
4673 4395 return JS_TRUE;
4674 45 if (pn->pn_extra != PNX_STRCAT)
4675 0 goto do_binary_op;
4676
4677 /* Ok, we're concatenating: convert non-string constant operands. */
4678 45 length = 0;
4679 300 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4680 255 if (!FoldType(cx, pn2, TOK_STRING))
4681 0 return JS_FALSE;
4682 /* XXX fold only if all operands convert to string */
4683 255 if (pn2->pn_type != TOK_STRING)
4684 0 return JS_TRUE;
4685 255 length += ATOM_TO_STRING(pn2->pn_atom)->length;
4686 }
4687
4688 /* Allocate a new buffer and string descriptor for the result. */
4689 45 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
4690 45 if (!chars)
4691 0 return JS_FALSE;
4692 45 str = js_NewString(cx, chars, length, 0);
4693 45 if (!str) {
4694 0 JS_free(cx, chars);
4695 0 return JS_FALSE;
4696 }
4697
4698 /* Fill the buffer, advancing chars and recycling kids as we go. */
4699 300 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
4700 255 str2 = ATOM_TO_STRING(pn2->pn_atom);
4701 255 length2 = str2->length;
4702 255 js_strncpy(chars, str2->chars, length2);
4703 255 chars += length2;
4704 }
4705 45 *chars = 0;
4706
4707 /* Atomize the result string and mutate pn to refer to it. */
4708 45 pn->pn_atom = js_AtomizeString(cx, str, 0);
4709 45 if (!pn->pn_atom)
4710 0 return JS_FALSE;
4711 45 pn->pn_type = TOK_STRING;
4712 45 pn->pn_op = JSOP_STRING;
4713 45 pn->pn_arity = PN_NULLARY;
4714 45 break;
4715 }
4716
4717 /* Handle a binary string concatenation. */
4718 JS_ASSERT(pn->pn_arity == PN_BINARY);
4719 13128 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
4720 JSString *left, *right, *str;
4721
4722 6802 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
4723 TOK_STRING)) {
4724 0 return JS_FALSE;
4725 }
4726 6802 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
4727 6794 return JS_TRUE;
4728 8 left = ATOM_TO_STRING(pn1->pn_atom);
4729 8 right = ATOM_TO_STRING(pn2->pn_atom);
4730 8 str = js_ConcatStrings(cx, left, right);
4731 8 if (!str)
4732 0 return JS_FALSE;
4733 8 pn->pn_atom = js_AtomizeString(cx, str, 0);
4734 8 if (!pn->pn_atom)
4735 0 return JS_FALSE;
4736 8 pn->pn_type = TOK_STRING;
4737 8 pn->pn_op = JSOP_STRING;
4738 8 pn->pn_arity = PN_NULLARY;
4739 8 RecycleTree(pn1, tc);
4740 8 RecycleTree(pn2, tc);
4741 8 break;
4742 }
4743
4744 /* Can't concatenate string literals, let's try numbers. */
4745 6326 goto do_binary_op;
4746
4747 case TOK_STAR:
4748 /* The * in 'import *;' parses as a nullary star node. */
4749 0 if (pn->pn_arity == PN_NULLARY)
4750 0 break;
4751 /* FALL THROUGH */
4752
4753 case TOK_SHOP:
4754 case TOK_MINUS:
4755 case TOK_DIVOP:
4756 6619 do_binary_op:
4757 6619 if (pn->pn_arity == PN_LIST) {
4758 JS_ASSERT(pn->pn_count > 2);
4759 0 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4760 0 if (!FoldType(cx, pn2, TOK_NUMBER))
4761 0 return JS_FALSE;
4762 /* XXX fold only if all operands convert to number */
4763 0 if (pn2->pn_type != TOK_NUMBER)
4764 0 break;
4765 }
4766 0 if (!pn2) {
4767 0 JSOp op = pn->pn_op;
4768
4769 0 pn2 = pn1->pn_next;
4770 0 pn3 = pn2->pn_next;
4771 0 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
4772 0 return JS_FALSE;
4773 0 while ((pn2 = pn3) != NULL) {
4774 0 pn3 = pn2->pn_next;
4775 0 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
4776 0 return JS_FALSE;
4777 }
4778 }
4779 } else {
4780 JS_ASSERT(pn->pn_arity == PN_BINARY);
4781 6619 if (!FoldType(cx, pn1, TOK_NUMBER) ||
4782 !FoldType(cx, pn2, TOK_NUMBER)) {
4783 0 return JS_FALSE;
4784 }
4785 6619 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
4786 0 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
4787 0 return JS_FALSE;
4788 }
4789 }
4790 6619 break;
4791
4792 case TOK_UNARYOP:
4793 2252 if (pn1->pn_type == TOK_NUMBER) {
4794 jsdouble d;
4795 int32 i;
4796
4797 /* Operate on one numeric constant. */
4798 1089 d = pn1->pn_dval;
4799 1089 switch (pn->pn_op) {
4800 case JSOP_BITNOT:
4801 0 if (!js_DoubleToECMAInt32(cx, d, &i))
4802 0 return JS_FALSE;
4803 0 d = ~i;
4804 0 break;
4805
4806 case JSOP_NEG:
4807 #ifdef HPUX
4808 /*
4809 * Negation of a zero doesn't produce a negative
4810 * zero on HPUX. Perform the operation by bit
4811 * twiddling.
4812 */
4813 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
4814 #else
4815 1089 d = -d;
4816 #endif
4817 1089 break;
4818
4819 case JSOP_POS:
4820 0 break;
4821
4822 case JSOP_NOT:
4823 0 pn->pn_type = TOK_PRIMARY;
4824 0 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
4825 0 pn->pn_arity = PN_NULLARY;
4826 /* FALL THROUGH */
4827
4828 default:
4829 /* Return early to dodge the common TOK_NUMBER code. */
4830 0 return JS_TRUE;
4831 }
4832 1089 pn->pn_type = TOK_NUMBER;
4833 1089 pn->pn_op = JSOP_NUMBER;
4834 1089 pn->pn_arity = PN_NULLARY;
4835 1089 pn->pn_dval = d;
4836 1089 RecycleTree(pn1, tc);
4837 }
4838 2252 break;
4839
4840 #if JS_HAS_XML_SUPPORT
4841 case TOK_XMLELEM:
4842 case TOK_XMLLIST:
4843 case TOK_XMLPTAGC:
4844 case TOK_XMLSTAGO:
4845 case TOK_XMLETAGO:
4846 case TOK_XMLNAME:
4847 0 if (pn->pn_arity == PN_LIST) {
4848 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
4849 0 if (!FoldXMLConstants(cx, pn, tc))
4850 0 return JS_FALSE;
4851 }
4852 0 break;
4853
4854 case TOK_AT:
4855 0 if (pn1->pn_type == TOK_XMLNAME) {
4856 jsval v;
4857 JSAtom *atom;
4858
4859 0 v = ATOM_KEY(pn1->pn_atom);
4860 0 if (!js_ToAttributeName(cx, &v))
4861 0 return JS_FALSE;
4862 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
4863 0 atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0);
4864 0 if (!atom)
4865 0 return JS_FALSE;
4866
4867 0 pn->pn_type = TOK_XMLNAME;
4868 0 pn->pn_op = JSOP_OBJECT;
4869 0 pn->pn_arity = PN_NULLARY;
4870 0 pn->pn_atom = atom;
4871 0 RecycleTree(pn1, tc);
4872 }
4873 break;
4874 #endif /* JS_HAS_XML_SUPPORT */
4875
4876 default:;
4877 }
4878
4879 2478788 return JS_TRUE;