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 ***** */
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"
77 #if JS_HAS_XML_SUPPORT
78 #include "jsxml.h"
79 #endif
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 */
88 typedef JSParseNode *
89 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
91 typedef JSParseNode *
92 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
93 JSBool allowCallSyntax);
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;
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
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
142 #ifdef METER_PARSENODES
143 static uint32 parsenodes = 0;
144 static uint32 maxparsenodes = 0;
145 static uint32 recyclednodes = 0;
146 #endif
148 static JSParseNode *
149 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
150 {
151 JSParseNode *next;
153 if (!pn)
154 return NULL;
155 JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */
156 next = pn->pn_next;
157 pn->pn_next = tc->nodeList;
158 tc->nodeList = pn;
159 #ifdef METER_PARSENODES
160 recyclednodes++;
161 #endif
162 return next;
163 }
165 static JSParseNode *
166 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
167 {
168 JSParseNode *pn;
170 pn = tc->nodeList;
171 if (!pn) {
172 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
173 if (!pn)
174 JS_ReportOutOfMemory(cx);
175 } else {
176 tc->nodeList = pn->pn_next;
178 /* Recycle immediate descendents only, to save work and working set. */
179 switch (pn->pn_arity) {
180 case PN_FUNC:
181 RecycleTree(pn->pn_body, tc);
182 break;
183 case PN_LIST:
184 if (pn->pn_head) {
185 /* XXX check for dup recycles in the list */
186 *pn->pn_tail = tc->nodeList;
187 tc->nodeList = pn->pn_head;
188 #ifdef METER_PARSENODES
189 recyclednodes += pn->pn_count;
190 #endif
191 }
192 break;
193 case PN_TERNARY:
194 RecycleTree(pn->pn_kid1, tc);
195 RecycleTree(pn->pn_kid2, tc);
196 RecycleTree(pn->pn_kid3, tc);
197 break;
198 case PN_BINARY:
199 RecycleTree(pn->pn_left, tc);
200 RecycleTree(pn->pn_right, tc);
201 break;
202 case PN_UNARY:
203 RecycleTree(pn->pn_kid, tc);
204 break;
205 case PN_NAME:
206 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 return pn;
220 }
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 {
229 JSParseNode *pn;
230 JSToken *tp;
232 pn = NewOrRecycledNode(cx, tc);
233 if (!pn)
234 return NULL;
235 tp = &CURRENT_TOKEN(ts);
236 pn->pn_type = tp->type;
237 pn->pn_pos = tp->pos;
238 pn->pn_op = JSOP_NOP;
239 pn->pn_arity = arity;
240 pn->pn_next = NULL;
241 #if JS_HAS_XML_SUPPORT
242 pn->pn_ts = ts;
243 #endif
244 return pn;
245 }
247 static JSParseNode *
248 NewBinary(JSContext *cx, JSTokenType tt,
249 JSOp op, JSParseNode *left, JSParseNode *right,
250 JSTreeContext *tc)
251 {
252 JSParseNode *pn, *pn1, *pn2;
254 if (!left || !right)
255 return NULL;
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 if (left->pn_type == tt &&
262 left->pn_op == op &&
263 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
264 if (left->pn_arity != PN_LIST) {
265 pn1 = left->pn_left, pn2 = left->pn_right;
266 left->pn_arity = PN_LIST;
267 PN_INIT_LIST_1(left, pn1);
268 PN_APPEND(left, pn2);
269 if (tt == TOK_PLUS) {
270 if (pn1->pn_type == TOK_STRING)
271 left->pn_extra |= PNX_STRCAT;
272 else if (pn1->pn_type != TOK_NUMBER)
273 left->pn_extra |= PNX_CANTFOLD;
274 if (pn2->pn_type == TOK_STRING)
275 left->pn_extra |= PNX_STRCAT;
276 else if (pn2->pn_type != TOK_NUMBER)
277 left->pn_extra |= PNX_CANTFOLD;
278 }
279 }
280 PN_APPEND(left, right);
281 left->pn_pos.end = right->pn_pos.end;
282 if (tt == TOK_PLUS) {
283 if (right->pn_type == TOK_STRING)
284 left->pn_extra |= PNX_STRCAT;
285 else if (right->pn_type != TOK_NUMBER)
286 left->pn_extra |= PNX_CANTFOLD;
287 }
288 return left;
289 }
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 if (tt == TOK_PLUS &&
299 left->pn_type == TOK_NUMBER &&
300 right->pn_type == TOK_NUMBER) {
301 left->pn_dval += right->pn_dval;
302 left->pn_pos.end = right->pn_pos.end;
303 RecycleTree(right, tc);
304 return left;
305 }
307 pn = NewOrRecycledNode(cx, tc);
308 if (!pn)
309 return NULL;
310 pn->pn_type = tt;
311 pn->pn_pos.begin = left->pn_pos.begin;
312 pn->pn_pos.end = right->pn_pos.end;
313 pn->pn_op = op;
314 pn->pn_arity = PN_BINARY;
315 pn->pn_left = left;
316 pn->pn_right = right;
317 pn->pn_next = NULL;
318 #if JS_HAS_XML_SUPPORT
319 pn->pn_ts = NULL;
320 #endif
321 return pn;
322 }
324 #if JS_HAS_GETTER_SETTER
325 static JSTokenType
326 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
327 {
328 JSAtom *atom;
329 JSRuntime *rt;
330 JSOp op;
331 const char *name;
333 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
334 atom = CURRENT_TOKEN(ts).t_atom;
335 rt = cx->runtime;
336 if (atom == rt->atomState.getterAtom)
337 op = JSOP_GETTER;
338 else if (atom == rt->atomState.setterAtom)
339 op = JSOP_SETTER;
340 else
341 return TOK_NAME;
342 if (js_PeekTokenSameLine(cx, ts) != tt)
343 return TOK_NAME;
344 (void) js_GetToken(cx, ts);
345 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
346 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 return TOK_ERROR;
352 }
353 CURRENT_TOKEN(ts).t_op = op;
354 name = js_AtomToPrintableString(cx, atom);
355 if (!name ||
356 !js_ReportCompileErrorNumber(cx, ts,
357 JSREPORT_TS |
358 JSREPORT_WARNING |
359 JSREPORT_STRICT,
360 JSMSG_DEPRECATED_USAGE,
361 name)) {
362 return TOK_ERROR;
363 }
364 return tt;
365 }
366 #endif
368 /*
369 * Parse a top-level JS script.
370 */
371 JS_FRIEND_API(JSParseNode *)
372 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
373 {
374 JSStackFrame *fp, frame;
375 JSTreeContext tc;
376 JSParseNode *pn;
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 fp = cx->fp;
384 if (!fp || !fp->varobj || fp->scopeChain != chain) {
385 memset(&frame, 0, sizeof frame);
386 frame.varobj = frame.scopeChain = chain;
387 if (cx->options & JSOPTION_VAROBJFIX) {
388 while ((chain = JS_GetParent(cx, chain)) != NULL)
389 frame.varobj = chain;
390 }
391 frame.down = fp;
392 if (fp) {
393 frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
394 JSFRAME_SCRIPT_OBJECT);
395 }
396 cx->fp = &frame;
397 }
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 JS_KEEP_ATOMS(cx->runtime);
408 TREE_CONTEXT_INIT(&tc);
409 pn = Statements(cx, ts, &tc);
410 if (pn) {
411 if (!js_MatchToken(cx, ts, TOK_EOF)) {
412 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
413 JSMSG_SYNTAX_ERROR);
414 pn = NULL;
415 } else {
416 pn->pn_type = TOK_LC;
417 if (!js_FoldConstants(cx, pn, &tc))
418 pn = NULL;
419 }
420 }
422 TREE_CONTEXT_FINISH(&tc);
423 JS_UNKEEP_ATOMS(cx->runtime);
424 cx->fp = fp;
425 return pn;
426 }
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 {
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
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 fp = cx->fp;
449 if (!fp || !fp->varobj || fp->scopeChain != chain) {
450 memset(&frame, 0, sizeof frame);
451 frame.varobj = frame.scopeChain = chain;
452 if (cx->options & JSOPTION_VAROBJFIX) {
453 while ((chain = JS_GetParent(cx, chain)) != NULL)
454 frame.varobj = chain;
455 }
456 frame.down = fp;
457 if (fp) {
458 frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
459 JSFRAME_SCRIPT_OBJECT);
460 }
461 cx->fp = &frame;
462 }
463 flags = cx->fp->flags;
464 cx->fp->flags = flags |
465 (JS_HAS_COMPILE_N_GO_OPTION(cx)
466 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
467 : JSFRAME_COMPILING);
469 /* Prevent GC activation while compiling. */
470 JS_KEEP_ATOMS(cx->runtime);
472 pn = Statements(cx, ts, &cg->treeContext);
473 if (!pn) {
474 ok = JS_FALSE;
475 } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
476 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
477 JSMSG_SYNTAX_ERROR);
478 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
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 ok = JS_TRUE;
498 }
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 JS_UNKEEP_ATOMS(cx->runtime);
508 cx->fp->flags = flags;
509 cx->fp = fp;
510 return ok;
511 }
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
522 static int
523 HasFinalReturn(JSParseNode *pn)
524 {
525 uintN rv, rv2, hasDefault;
526 JSParseNode *pn2, *pn3;
528 switch (pn->pn_type) {
529 case TOK_LC:
530 if (!pn->pn_head)
531 return ENDS_IN_OTHER;
532 return HasFinalReturn(PN_LAST(pn));
534 case TOK_IF:
535 rv = HasFinalReturn(pn->pn_kid2);
536 if (pn->pn_kid3)
537 rv &= HasFinalReturn(pn->pn_kid3);
538 return rv;
540 #if JS_HAS_SWITCH_STATEMENT
541 case TOK_SWITCH:
542 rv = ENDS_IN_RETURN;
543 hasDefault = ENDS_IN_OTHER;
544 for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
545 if (pn2->pn_type == TOK_DEFAULT)
546 hasDefault = ENDS_IN_RETURN;
547 pn3 = pn2->pn_right;
548 JS_ASSERT(pn3->pn_type == TOK_LC);
549 if (pn3->pn_head) {
550 rv2 = HasFinalReturn(PN_LAST(pn3));
551 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
552 /* Falling through to next case or default. */;
553 else
554 rv &= rv2;
555 }
556 }
557 /* If a final switch has no default case, we judge it harshly. */
558 rv &= hasDefault;
559 return rv;
560 #endif /* JS_HAS_SWITCH_STATEMENT */
562 case TOK_BREAK:
563 return ENDS_IN_BREAK;
565 case TOK_WITH:
566 return HasFinalReturn(pn->pn_right);
568 case TOK_RETURN:
569 return ENDS_IN_RETURN;
571 case TOK_COLON:
572 return HasFinalReturn(pn->pn_expr);
574 #if JS_HAS_EXCEPTIONS
575 case TOK_THROW:
576 return ENDS_IN_RETURN;
578 case TOK_TRY:
579 /* If we have a finally block that returns, we are done. */
580 if (pn->pn_kid3) {
581 rv = HasFinalReturn(pn->pn_kid3);
582 if (rv == ENDS_IN_RETURN)
583 return rv;
584 }
586 /* Else check the try block and any and all catch statements. */
587 rv = HasFinalReturn(pn->pn_kid1);
588 if (pn->pn_kid2)
589 rv &= HasFinalReturn(pn->pn_kid2);
590 return rv;
592 case TOK_CATCH:
593 /* Check this block's code and iterate over further catch blocks. */
594 rv = HasFinalReturn(pn->pn_kid3);
595 for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
596 rv &= HasFinalReturn(pn2->pn_kid3);
597 return rv;
598 #endif
600 default:
601 return ENDS_IN_OTHER;
602 }
603 }
605 static JSBool
606 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
607 {
608 JSFunction *fun;
609 JSBool ok;
611 fun = cx->fp->fun;
612 if (fun->atom) {
613 char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
614 ok = js_ReportCompileErrorNumber(cx, ts,
615 JSREPORT_TS |
616 JSREPORT_WARNING |
617 JSREPORT_STRICT,
618 JSMSG_NO_RETURN_VALUE, name);
619 } else {
620 ok = js_ReportCompileErrorNumber(cx, ts,
621 JSREPORT_TS |
622 JSREPORT_WARNING |
623 JSREPORT_STRICT,
624 JSMSG_ANON_NO_RETURN_VALUE);
625 }
626 return ok;
627 }
629 static JSBool
630 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
631 {
632 return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
633 }
635 static JSParseNode *
636 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
637 JSTreeContext *tc)
638 {
639 JSStackFrame *fp, frame;
640 JSObject *funobj;
641 uintN oldflags;
642 JSParseNode *pn;
644 fp = cx->fp;
645 funobj = fun->object;
646 if (!fp || fp->fun != fun || fp->varobj != funobj ||
647 fp->scopeChain != funobj) {
648 memset(&frame, 0, sizeof frame);
649 frame.fun = fun;
650 frame.varobj = frame.scopeChain = funobj;
651 frame.down = fp;
652 if (fp)
653 frame.flags = fp->flags & JSFRAME_COMPILE_N_GO;
654 cx->fp = &frame;
655 }
657 oldflags = tc->flags;
658 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
659 tc->flags |= TCF_IN_FUNCTION;
660 pn = Statements(cx, ts, tc);
662 /* Check for falling off the end of a function that returns a value. */
663 if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
664 if (!CheckFinalReturn(cx, ts, pn))
665 pn = NULL;
666 }
668 cx->fp = fp;
669 tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS));
670 return pn;
671 }
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 {
680 JSArenaPool codePool, notePool;
681 JSCodeGenerator funcg;
682 JSStackFrame *fp, frame;
683 JSObject *funobj;
684 JSParseNode *pn;
685 JSBool ok;
687 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
688 JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote));
689 if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool,
690 ts->filename, ts->lineno,
691 ts->principals)) {
692 return JS_FALSE;
693 }
695 /* Prevent GC activation while compiling. */
696 JS_KEEP_ATOMS(cx->runtime);
698 /* Push a JSStackFrame for use by FunctionBody. */
699 fp = cx->fp;
700 funobj = fun->object;
701 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
702 fp->scopeChain != funobj));
703 memset(&frame, 0, sizeof frame);
704 frame.fun = fun;
705 frame.varobj = frame.scopeChain = funobj;
706 frame.down = fp;
707 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
708 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
709 : JSFRAME_COMPILING;
710 cx->fp = &frame;
712 /* Ensure that the body looks like a block statement to js_EmitTree. */
713 CURRENT_TOKEN(ts).type = TOK_LC;
714 pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
715 if (!pn) {
716 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 fun->u.script = js_NewScriptFromCG(cx, &funcg, fun);
723 if (!fun->u.script) {
724 ok = JS_FALSE;
725 } else {
726 fun->interpreted = JS_TRUE;
727 if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
728 fun->flags |= JSFUN_HEAVYWEIGHT;
729 ok = JS_TRUE;
730 }
731 }
733 /* Restore saved state and release code generation arenas. */
734 cx->fp = fp;
735 JS_UNKEEP_ATOMS(cx->runtime);
736 js_FinishCodeGenerator(cx, &funcg);
737 JS_FinishArenaPool(&codePool);
738 JS_FinishArenaPool(¬ePool);
739 return ok;
740 }
742 static JSParseNode *
743 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
744 JSBool lambda)
745 {
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;
758 /* Make a TOK_FUNCTION node. */
759 #if JS_HAS_GETTER_SETTER
760 op = CURRENT_TOKEN(ts).t_op;
761 #endif
762 pn = NewParseNode(cx, ts, PN_FUNC, tc);
763 if (!pn)
764 return NULL;
766 /* Scan the optional function name into funAtom. */
767 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
776 /* Find the nearest variable-declaring scope and use it as our parent. */
777 fp = cx->fp;
778 varobj = fp->varobj;
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 if (!lambda && funAtom) {
785 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
786 if (ale) {
787 prevop = ALE_JSOP(ale);
788 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
789 const char *name = js_AtomToPrintableString(cx, funAtom);
790 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 return NULL;
806 }
807 }
808 if (tc->topStmt && prevop == JSOP_DEFVAR)
809 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
810 } else {
811 ale = js_IndexAtom(cx, funAtom, &tc->decls);
812 if (!ale)
813 return NULL;
814 }
815 ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
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 if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
827 JSScopeProperty *sprop;
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 if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom),
836 &pobj, &prop)) {
837 return NULL;
838 }
839 if (prop)
840 OBJ_DROP_PROPERTY(cx, pobj, prop);
841 sprop = NULL;
842 if (!prop ||
843 pobj != varobj ||
844 (sprop = (JSScopeProperty *)prop,
845 sprop->getter != js_GetLocalVariable)) {
846 uintN sflags;
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 sflags = (sprop && sprop->getter == js_GetArgument)
853 ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID
854 : SPROP_HAS_SHORTID;
855 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 return NULL;
862 }
863 if (fp->fun->nvars == JS_BITMASK(16)) {
864 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
865 JSMSG_TOO_MANY_FUN_VARS);
866 return NULL;
867 }
868 fp->fun->nvars++;
869 }
870 }
871 #endif
872 }
874 fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
875 funAtom);
876 if (!fun)
877 return NULL;
878 #if JS_HAS_GETTER_SETTER
879 if (op != JSOP_NOP)
880 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
881 #endif
884 /*
885 * Set interpreted early so js_EmitTree can test it to decide whether to
886 * eliminate useless expressions.
887 */
888 fun->interpreted = JS_TRUE;
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 objAtom = js_AtomizeObject(cx, fun->object, 0);
901 if (!objAtom)
902 return NULL;
904 /* Now parse formal argument list and compute fun->nargs. */
905 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
906 if (!js_MatchToken(cx, ts, TOK_RP)) {
907 do {
908 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
909 argAtom = CURRENT_TOKEN(ts).t_atom;
910 pobj = NULL;
911 if (!js_LookupHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom),
912 &pobj, &prop)) {
913 return NULL;
914 }
915 dupflag = 0;
916 if (prop) {
917 ok = JS_TRUE;
918 if (pobj == fun->object &&
919 ((JSScopeProperty *) prop)->getter == js_GetArgument) {
920 const char *name = js_AtomToPrintableString(cx, argAtom);
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 ok = name &&
929 js_ReportCompileErrorNumber(cx, ts,
930 JSREPORT_TS |
931 JSREPORT_WARNING |
932 JSREPORT_STRICT,
933 JSMSG_DUPLICATE_FORMAL,
934 name);
936 dupflag = SPROP_IS_DUPLICATE;
937 }
938 OBJ_DROP_PROPERTY(cx, pobj, prop);
939 if (!ok)
940 return NULL;
941 prop = NULL;
942 }
943 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 return NULL;
950 }
951 if (fun->nargs == JS_BITMASK(16)) {
952 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
953 JSMSG_TOO_MANY_FUN_ARGS);
954 return NULL;
955 }
956 fun->nargs++;
957 } while (js_MatchToken(cx, ts, TOK_COMMA));
959 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
960 }
962 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
963 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
965 TREE_CONTEXT_INIT(&funtc);
966 body = FunctionBody(cx, ts, fun, &funtc);
967 if (!body)
968 return NULL;
970 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
971 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
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 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
982 fun->flags |= JSFUN_HEAVYWEIGHT;
983 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 if ((!lambda && funAtom && tc->topStmt) ||
992 (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
993 tc->flags |= TCF_FUN_HEAVYWEIGHT;
994 }
995 }
996 #endif
998 result = pn;
999 #if JS_HAS_LEXICAL_CLOSURE
1000 if (lambda) {
1001 /*
1002 * ECMA ed. 3 standard: function expression, possibly anonymous.
1003 */
1004 op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1005 } 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 result = NewParseNode(cx, ts, PN_UNARY, tc);
1013 if (!result)
1014 return NULL;
1015 result->pn_type = TOK_SEMI;
1016 result->pn_pos = pn->pn_pos;
1017 result->pn_kid = pn;
1018 op = JSOP_ANONFUNOBJ;
1019 } 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 op = JSOP_CLOSURE;
1027 } else
1028 #endif
1029 op = JSOP_NOP;
1031 pn->pn_funAtom = objAtom;
1032 pn->pn_op = op;
1033 pn->pn_body = body;
1034 pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS);
1035 pn->pn_tryCount = funtc.tryCount;
1036 TREE_CONTEXT_FINISH(&funtc);
1037 return result;
1038 }
1040 static JSParseNode *
1041 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1042 {
1043 return FunctionDef(cx, ts, tc, JS_FALSE);
1044 }
1046 #if JS_HAS_LEXICAL_CLOSURE
1047 static JSParseNode *
1048 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1049 {
1050 return FunctionDef(cx, ts, tc, JS_TRUE);
1051 }
1052 #endif
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 {
1062 JSParseNode *pn, *pn2;
1063 JSTokenType tt;
1065 CHECK_RECURSION();
1067 pn = NewParseNode(cx, ts, PN_LIST, tc);
1068 if (!pn)
1069 return NULL;
1070 PN_INIT_LIST(pn);
1072 ts->flags |= TSF_OPERAND;
1073 while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
1074 ts->flags &= ~TSF_OPERAND;
1075 pn2 = Statement(cx, ts, tc);
1076 if (!pn2) {
1077 if (ts->flags & TSF_EOF)
1078 ts->flags |= TSF_UNEXPECTED_EOF;
1079 return NULL;
1080 }
1081 ts->flags |= TSF_OPERAND;
1083 /* If compiling top-level statements, emit as we go to save space. */
1084 if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1085 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 tt = js_PeekToken(cx, ts);
1093 if ((tt == TOK_EOF || tt == TOK_RC) &&
1094 !CheckFinalReturn(cx, ts, pn2)) {
1095 tt = TOK_ERROR;
1096 break;
1097 }
1099 /*
1100 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1101 * CheckFinalReturn again.
1102 */
1103 tc->flags &= ~TCF_RETURN_EXPR;
1104 }
1105 if (!js_FoldConstants(cx, pn2, tc) ||
1106 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1107 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1108 tt = TOK_ERROR;
1109 break;
1110 }
1111 RecycleTree(pn2, tc);
1112 } else {
1113 PN_APPEND(pn, pn2);
1114 }
1115 }
1116 ts->flags &= ~TSF_OPERAND;
1117 if (tt == TOK_ERROR)
1118 return NULL;
1120 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1121 return pn;
1122 }
1124 static JSParseNode *
1125 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1126 {
1127 JSParseNode *pn, *pn2;
1129 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1130 pn = Expr(cx, ts, tc);
1131 if (!pn)
1132 return NULL;
1133 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
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 if (pn->pn_type == TOK_ASSIGN &&
1141 pn->pn_op == JSOP_NOP &&
1142 pn->pn_right->pn_type > TOK_EQOP)
1143 {
1144 JSBool rewrite = !JS_VERSION_IS_ECMA(cx);
1145 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 return NULL;
1154 }
1155 if (rewrite) {
1156 pn->pn_type = TOK_EQOP;
1157 pn->pn_op = (JSOp)cx->jsop_eq;
1158 pn2 = pn->pn_left;
1159 switch (pn2->pn_op) {
1160 case JSOP_SETNAME:
1161 pn2->pn_op = JSOP_NAME;
1162 break;
1163 case JSOP_SETPROP:
1164 pn2->pn_op = JSOP_GETPROP;
1165 break;
1166 case JSOP_SETELEM:
1167 pn2->pn_op = JSOP_GETELEM;
1168 break;
1169 default:
1170 JS_ASSERT(0);
1171 }
1172 }
1173 }
1174 return pn;
1175 }
1177 static JSBool
1178 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1179 {
1180 JSAtom *label;
1181 #if JS_HAS_LABEL_STATEMENT
1182 JSTokenType tt;
1184 tt = js_PeekTokenSameLine(cx, ts);
1185 if (tt == TOK_ERROR)
1186 return JS_FALSE;
1187 if (tt == TOK_NAME) {
1188 (void) js_GetToken(cx, ts);
1189 label = CURRENT_TOKEN(ts).t_atom;
1190 } else {
1191 label = NULL;
1192 }
1193 #else
1194 label = NULL;
1195 #endif
1196 pn->pn_atom = label;
1197 return JS_TRUE;
1198 }
1200 #if JS_HAS_EXPORT_IMPORT
1201 static JSParseNode *
1202 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1203 {
1204 JSParseNode *pn, *pn2;
1205 JSTokenType tt;
1207 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1208 pn = NewParseNode(cx, ts, PN_NAME, tc);
1209 if (!pn)
1210 return NULL;
1211 pn->pn_op = JSOP_NAME;
1212 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1213 pn->pn_expr = NULL;
1214 pn->pn_slot = -1;
1215 pn->pn_attrs = 0;
1217 ts->flags |= TSF_OPERAND;
1218 while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1219 ts->flags &= ~TSF_OPERAND;
1220 if (pn->pn_op == JSOP_IMPORTALL)
1221 goto bad_import;
1223 if (tt == TOK_DOT) {
1224 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1225 if (!pn2)
1226 return NULL;
1227 if (js_MatchToken(cx, ts, TOK_STAR)) {
1228 pn2->pn_op = JSOP_IMPORTALL;
1229 pn2->pn_atom = NULL;
1230 } else {
1231 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1232 pn2->pn_op = JSOP_GETPROP;
1233 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1234 pn2->pn_slot = -1;
1235 pn2->pn_attrs = 0;
1236 }
1237 pn2->pn_expr = pn;
1238 pn2->pn_pos.begin = pn->pn_pos.begin;
1239 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1240 } else {
1241 /* Make a TOK_LB binary node. */
1242 pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc);
1243 if (!pn2)
1244 return NULL;
1246 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1247 }
1249 pn = pn2;
1250 ts->flags |= TSF_OPERAND;
1251 }
1252 ts->flags &= ~TSF_OPERAND;
1253 if (tt == TOK_ERROR)
1254 return NULL;
1255 js_UngetToken(ts);
1257 switch (pn->pn_op) {
1258 case JSOP_GETPROP:
1259 pn->pn_op = JSOP_IMPORTPROP;
1260 break;
1261 case JSOP_GETELEM:
1262 pn->pn_op = JSOP_IMPORTELEM;
1263 break;
1264 case JSOP_IMPORTALL:
1265 break;
1266 default:
1267 goto bad_import;
1268 }
1269 return pn;
1271 bad_import:
1272 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1273 JSMSG_BAD_IMPORT);
1274 return NULL;
1275 }
1276 #endif /* JS_HAS_EXPORT_IMPORT */
1278 extern const char js_with_statement_str[];
1280 static JSParseNode *
1281 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1282 {
1283 JSTokenType tt;
1284 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1285 JSStmtInfo stmtInfo, *stmt, *stmt2;
1286 JSAtom *label;
1288 CHECK_RECURSION();
1290 ts->flags |= TSF_OPERAND;
1291 tt = js_GetToken(cx, ts);
1292 ts->flags &= ~TSF_OPERAND;
1294 #if JS_HAS_GETTER_SETTER
1295 if (tt == TOK_NAME) {
1296 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1297 if (tt == TOK_ERROR)
1298 return NULL;
1299 }
1300 #endif
1302 switch (tt) {
1303 #if JS_HAS_EXPORT_IMPORT
1304 case TOK_EXPORT:
1305 pn = NewParseNode(cx, ts, PN_LIST, tc);
1306 if (!pn)
1307 return NULL;
1308 PN_INIT_LIST(pn);
1309 if (js_MatchToken(cx, ts, TOK_STAR)) {
1310 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
1311 if (!pn2)
1312 return NULL;
1313 PN_APPEND(pn, pn2);
1314 } else {
1315 do {
1316 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1317 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
1318 if (!pn2)
1319 return NULL;
1320 pn2->pn_op = JSOP_NAME;
1321 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1322 pn2->pn_expr = NULL;
1323 pn2->pn_slot = -1;
1324 pn2->pn_attrs = 0;
1325 PN_APPEND(pn, pn2);
1326 } while (js_MatchToken(cx, ts, TOK_COMMA));
1327 }
1328 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1329 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1330 break;
1332 case TOK_IMPORT:
1333 pn = NewParseNode(cx, ts, PN_LIST, tc);
1334 if (!pn)
1335 return NULL;
1336 PN_INIT_LIST(pn);
1337 do {
1338 pn2 = ImportExpr(cx, ts, tc);
1339 if (!pn2)
1340 return NULL;
1341 PN_APPEND(pn, pn2);
1342 } while (js_MatchToken(cx, ts, TOK_COMMA));
1343 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1344 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1345 break;
1346 #endif /* JS_HAS_EXPORT_IMPORT */
1348 case TOK_FUNCTION:
1349 #if JS_HAS_XML_SUPPORT
1350 if (js_PeekToken(cx, ts) == TOK_DBLCOLON)
1351 goto expression;
1352 #endif
1353 return FunctionStmt(cx, ts, tc);
1355 case TOK_IF:
1356 /* An IF node has three kids: condition, then, and optional else. */
1357 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
1358 if (!pn)
1359 return NULL;
1360 pn1 = Condition(cx, ts, tc);
1361 if (!pn1)
1362 return NULL;
1363 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1364 pn2 = Statement(cx, ts, tc);
1365 if (!pn2)
1366 return NULL;
1367 if (js_MatchToken(cx, ts, TOK_ELSE)) {
1368 stmtInfo.type = STMT_ELSE;
1369 pn3 = Statement(cx, ts, tc);
1370 if (!pn3)
1371 return NULL;
1372 pn->pn_pos.end = pn3->pn_pos.end;
1373 } else {
1374 pn3 = NULL;
1375 pn->pn_pos.end = pn2->pn_pos.end;
1376 }
1377 js_PopStatement(tc);
1378 pn->pn_kid1 = pn1;
1379 pn->pn_kid2 = pn2;
1380 pn->pn_kid3 = pn3;
1381 return pn;
1383 #if JS_HAS_SWITCH_STATEMENT
1384 case TOK_SWITCH:
1385 {
1386 JSParseNode *pn5;
1387 JSBool seenDefault = JS_FALSE;
1389 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1390 if (!pn)
1391 return NULL;
1392 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1394 /* pn1 points to the switch's discriminant. */
1395 pn1 = Expr(cx, ts, tc);
1396 if (!pn1)
1397 return NULL;
1399 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1400 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1402 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1403 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
1404 if (!pn2)
1405 return NULL;
1406 PN_INIT_LIST(pn2);
1408 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1410 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1411 switch (tt) {
1412 case TOK_DEFAULT:
1413 if (seenDefault) {
1414 js_ReportCompileErrorNumber(cx, ts,
1415 JSREPORT_TS | JSREPORT_ERROR,
1416 JSMSG_TOO_MANY_DEFAULTS);
1417 return NULL;
1418 }
1419 seenDefault = JS_TRUE;
1420 /* fall through */
1422 case TOK_CASE:
1423 pn3 = NewParseNode(cx, ts, PN_BINARY, tc);
1424 if (!pn3)
1425 return NULL;
1426 if (tt == TOK_DEFAULT) {
1427 pn3->pn_left = NULL;
1428 } else {
1429 pn3->pn_left = Expr(cx, ts, tc);
1430 if (!pn3->pn_left)
1431 return NULL;
1432 }
1433 PN_APPEND(pn2, pn3);
1434 if (pn2->pn_count == JS_BIT(16)) {
1435 js_ReportCompileErrorNumber(cx, ts,
1436 JSREPORT_TS | JSREPORT_ERROR,
1437 JSMSG_TOO_MANY_CASES);
1438 return NULL;
1439 }
1440 break;
1442 case TOK_ERROR:
1443 return NULL;
1445 default:
1446 js_ReportCompileErrorNumber(cx, ts,
1447 JSREPORT_TS | JSREPORT_ERROR,
1448 JSMSG_BAD_SWITCH);
1449 return NULL;
1450 }
1451 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1453 pn4 = NewParseNode(cx, ts, PN_LIST, tc);
1454 if (!pn4)
1455 return NULL;
1456 pn4->pn_type = TOK_LC;
1457 PN_INIT_LIST(pn4);
1458 ts->flags |= TSF_OPERAND;
1459 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1460 tt != TOK_CASE && tt != TOK_DEFAULT) {
1461 ts->flags &= ~TSF_OPERAND;
1462 if (tt == TOK_ERROR)
1463 return NULL;
1464 pn5 = Statement(cx, ts, tc);
1465 if (!pn5)
1466 return NULL;
1467 pn4->pn_pos.end = pn5->pn_pos.end;
1468 PN_APPEND(pn4, pn5);
1469 ts->flags |= TSF_OPERAND;
1470 }
1471 ts->flags &= ~TSF_OPERAND;
1473 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1474 if (pn4->pn_head)
1475 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1476 pn3->pn_pos.end = pn4->pn_pos.end;
1477 pn3->pn_right = pn4;
1478 }
1480 js_PopStatement(tc);
1482 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1483 pn->pn_kid1 = pn1;
1484 pn->pn_kid2 = pn2;
1485 return pn;
1486 }
1487 #endif /* JS_HAS_SWITCH_STATEMENT */
1489 case TOK_WHILE:
1490 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1491 if (!pn)
1492 return NULL;
1493 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1494 pn2 = Condition(cx, ts, tc);
1495 if (!pn2)
1496 return NULL;
1497 pn->pn_left = pn2;
1498 pn2 = Statement(cx, ts, tc);
1499 if (!pn2)
1500 return NULL;
1501 js_PopStatement(tc);
1502 pn->pn_pos.end = pn2->pn_pos.end;
1503 pn->pn_right = pn2;
1504 return pn;
1506 #if JS_HAS_DO_WHILE_LOOP
1507 case TOK_DO:
1508 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1509 if (!pn)
1510 return NULL;
1511 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1512 pn2 = Statement(cx, ts, tc);
1513 if (!pn2)
1514 return NULL;
1515 pn->pn_left = pn2;
1516 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1517 pn2 = Condition(cx, ts, tc);
1518 if (!pn2)
1519 return NULL;
1520 js_PopStatement(tc);
1521 pn->pn_pos.end = pn2->pn_pos.end;
1522 pn->pn_right = pn2;
1523 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 (void) js_MatchToken(cx, ts, TOK_SEMI);
1530 return pn;
1531 }
1532 break;
1533 #endif /* JS_HAS_DO_WHILE_LOOP */
1535 case TOK_FOR:
1536 /* A FOR node is binary, left is loop control and right is the body. */
1537 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1538 if (!pn)
1539 return NULL;
1540 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1542 #if JS_HAS_XML_SUPPORT
1543 pn->pn_op = JSOP_NOP;
1544 if (js_MatchToken(cx, ts, TOK_NAME)) {
1545 if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
1546 pn->pn_op = JSOP_FOREACH;
1547 else
1548 js_UngetToken(ts);
1549 }
1550 #endif
1552 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1553 ts->flags |= TSF_OPERAND;
1554 tt = js_PeekToken(cx, ts);
1555 ts->flags &= ~TSF_OPERAND;
1556 if (tt == TOK_SEMI) {
1557 #if JS_HAS_XML_SUPPORT
1558 if (pn->pn_op == JSOP_FOREACH)
1559 goto bad_for_each;
1560 #endif
1562 /* No initializer -- set first kid of left sub-node to null. */
1563 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 tc->flags |= TCF_IN_FOR_INIT;
1577 #endif /* JS_HAS_IN_OPERATOR */
1578 if (tt == TOK_VAR) {
1579 (void) js_GetToken(cx, ts);
1580 pn1 = Variables(cx, ts, tc);
1581 } else {
1582 pn1 = Expr(cx, ts, tc);
1583 }
1584 #if JS_HAS_IN_OPERATOR
1585 tc->flags &= ~TCF_IN_FOR_INIT;
1586 #endif /* JS_HAS_IN_OPERATOR */
1587 if (!pn1)
1588 return NULL;
1589 }
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 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1598 stmtInfo.type = STMT_FOR_IN_LOOP;
1600 /* Check that the left side of the 'in' is valid. */
1601 while (pn1->pn_type == TOK_RP)
1602 pn1 = pn1->pn_kid;
1603 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 js_ReportCompileErrorNumber(cx, ts,
1616 JSREPORT_TS | JSREPORT_ERROR,
1617 JSMSG_BAD_FOR_LEFTSIDE);
1618 return NULL;
1619 }
1621 if (pn1->pn_type == TOK_VAR) {
1622 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
1623 pn1->pn_extra |= PNX_FORINVAR;
1625 /* Generate a final POP only if the var has an initializer. */
1626 pn2 = pn1->pn_head;
1627 if (pn2->pn_expr)
1628 pn1->pn_extra |= PNX_POPVAR;
1629 } else {
1630 pn2 = pn1;
1631 #if JS_HAS_LVALUE_RETURN
1632 if (pn2->pn_type == TOK_LP)
1633 pn2->pn_op = JSOP_SETCALL;
1634 #endif
1635 #if JS_HAS_XML_SUPPORT
1636 if (pn2->pn_type == TOK_UNARYOP)
1637 pn2->pn_op = JSOP_BINDXMLNAME;
1638 #endif
1639 }
1641 /* Beware 'for (arguments in ...)' with or without a 'var'. */
1642 if (pn2->pn_type == TOK_NAME &&
1643 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1644 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1645 }
1647 /* Parse the object expression as the right operand of 'in'. */
1648 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1649 if (!pn2)
1650 return NULL;
1651 pn->pn_left = pn2;
1652 } else {
1653 #if JS_HAS_XML_SUPPORT
1654 if (pn->pn_op == JSOP_FOREACH)
1655 goto bad_for_each;
1656 #endif
1658 /* Parse the loop condition or null into pn2. */
1659 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1660 ts->flags |= TSF_OPERAND;
1661 tt = js_PeekToken(cx, ts);
1662 ts->flags &= ~TSF_OPERAND;
1663 if (tt == TOK_SEMI) {
1664 pn2 = NULL;
1665 } else {
1666 pn2 = Expr(cx, ts, tc);
1667 if (!pn2)
1668 return NULL;
1669 }
1671 /* Parse the update expression or null into pn3. */
1672 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1673 ts->flags |= TSF_OPERAND;
1674 tt = js_PeekToken(cx, ts);
1675 ts->flags &= ~TSF_OPERAND;
1676 if (tt == TOK_RP) {
1677 pn3 = NULL;
1678 } else {
1679 pn3 = Expr(cx, ts, tc);
1680 if (!pn3)
1681 return NULL;
1682 }
1684 /* Build the RESERVED node to use as the left kid of pn. */
1685 pn4 = NewParseNode(cx, ts, PN_TERNARY, tc);
1686 if (!pn4)
1687 return NULL;
1688 pn4->pn_type = TOK_RESERVED;
1689 pn4->pn_op = JSOP_NOP;
1690 pn4->pn_kid1 = pn1;
1691 pn4->pn_kid2 = pn2;
1692 pn4->pn_kid3 = pn3;
1693 pn->pn_left = pn4;
1694 }
1696 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1698 /* Parse the loop body into pn->pn_right. */
1699 pn2 = Statement(cx, ts, tc);
1700 if (!pn2)
1701 return NULL;
1702 pn->pn_right = pn2;
1703 js_PopStatement(tc);
1705 /* Record the absolute line number for source note emission. */
1706 pn->pn_pos.end = pn2->pn_pos.end;
1707 return pn;
1709 #if JS_HAS_XML_SUPPORT
1710 bad_for_each:
1711 js_ReportCompileErrorNumber(cx, pn,
1712 JSREPORT_PN | JSREPORT_ERROR,
1713 JSMSG_BAD_FOR_EACH_LOOP);
1714 return NULL;
1715 #endif
1717 #if JS_HAS_EXCEPTIONS
1718 case TOK_TRY: {
1719 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 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
1739 pn->pn_op = JSOP_NOP;
1741 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1742 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1743 pn->pn_kid1 = Statements(cx, ts, tc);
1744 if (!pn->pn_kid1)
1745 return NULL;
1746 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1747 js_PopStatement(tc);
1749 catchtail = pn;
1750 while (js_PeekToken(cx, ts) == TOK_CATCH) {
1751 /* check for another catch after unconditional catch */
1752 if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1753 js_ReportCompileErrorNumber(cx, ts,
1754 JSREPORT_TS | JSREPORT_ERROR,
1755 JSMSG_CATCH_AFTER_GENERAL);
1756 return NULL;
1757 }
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 (void) js_GetToken(cx, ts); /* eat `catch' */
1766 pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
1767 if (!pn2)
1768 return NULL;
1770 /*
1771 * We use a PN_NAME for the discriminant (catchguard) node
1772 * with the actual discriminant code in the initializer spot
1773 */
1774 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1775 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1776 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
1777 if (!pn3)
1778 return NULL;
1780 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1781 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 if (js_PeekToken(cx, ts) == TOK_IF) {
1788 (void)js_GetToken(cx, ts); /* eat `if' */
1789 pn3->pn_expr = Expr(cx, ts, tc);
1790 if (!pn3->pn_expr)
1791 return NULL;
1792 }
1793 #endif
1794 pn2->pn_kid1 = pn3;
1796 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1798 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1799 js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1800 stmtInfo.label = pn3->pn_atom;
1801 pn2->pn_kid3 = Statements(cx, ts, tc);
1802 if (!pn2->pn_kid3)
1803 return NULL;
1804 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1805 js_PopStatement(tc);
1807 catchtail = catchtail->pn_kid2 = pn2;
1808 }
1809 catchtail->pn_kid2 = NULL;
1811 if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1812 tc->tryCount++;
1813 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1814 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1815 pn->pn_kid3 = Statements(cx, ts, tc);
1816 if (!pn->pn_kid3)
1817 return NULL;
1818 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1819 js_PopStatement(tc);
1820 } else {
1821 pn->pn_kid3 = NULL;
1822 }
1823 if (!pn->pn_kid2 && !pn->pn_kid3) {
1824 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1825 JSMSG_CATCH_OR_FINALLY);
1826 return NULL;
1827 }
1828 tc->tryCount++;
1829 return pn;
1830 }
1832 case TOK_THROW:
1833 pn = NewParseNode(cx, ts, PN_UNARY, tc);
1834 if (!pn)
1835 return NULL;
1837 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
1838 ts->flags |= TSF_OPERAND;
1839 tt = js_PeekTokenSameLine(cx, ts);
1840 ts->flags &= ~TSF_OPERAND;
1841 if (tt == TOK_ERROR)
1842 return NULL;
1843 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
1844 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1845 JSMSG_SYNTAX_ERROR);
1846 return NULL;
1847 }
1849 pn2 = Expr(cx, ts, tc);
1850 if (!pn2)
1851 return NULL;
1852 pn->pn_pos.end = pn2->pn_pos.end;
1853 pn->pn_op = JSOP_THROW;
1854 pn->pn_kid = pn2;
1855 break;
1857 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1858 case TOK_CATCH:
1859 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1860 JSMSG_CATCH_WITHOUT_TRY);
1861 return NULL;
1863 case TOK_FINALLY:
1864 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1865 JSMSG_FINALLY_WITHOUT_TRY);
1866 return NULL;
1868 #endif /* JS_HAS_EXCEPTIONS */
1870 case TOK_BREAK:
1871 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
1872 if (!pn)
1873 return NULL;
1874 if (!MatchLabel(cx, ts, pn))
1875 return NULL;
1876 stmt = tc->topStmt;
1877 label = pn->pn_atom;
1878 if (label) {
1879 for (; ; stmt = stmt->down) {
1880 if (!stmt) {
1881 js_ReportCompileErrorNumber(cx, ts,
1882 JSREPORT_TS | JSREPORT_ERROR,
1883 JSMSG_LABEL_NOT_FOUND);
1884 return NULL;
1885 }
1886 if (stmt->type == STMT_LABEL && stmt->label == label)
1887 break;
1888 }
1889 } else {
1890 for (; ; stmt = stmt->down) {
1891 if (!stmt) {
1892 js_ReportCompileErrorNumber(cx, ts,
1893 JSREPORT_TS | JSREPORT_ERROR,
1894 JSMSG_TOUGH_BREAK);
1895 return NULL;
1896 }
1897 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1898 break;
1899 }
1900 }
1901 if (label)
1902 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1903 break;
1905 case TOK_CONTINUE:
1906 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
1907 if (!pn)
1908 return NULL;
1909 if (!MatchLabel(cx, ts, pn))
1910 return NULL;
1911 stmt = tc->topStmt;
1912 label = pn->pn_atom;
1913 if (label) {
1914 for (stmt2 = NULL; ; stmt = stmt->down) {
1915 if (!stmt) {
1916 js_ReportCompileErrorNumber(cx, ts,
1917 JSREPORT_TS | JSREPORT_ERROR,
1918 JSMSG_LABEL_NOT_FOUND);
1919 return NULL;
1920 }
1921 if (stmt->type == STMT_LABEL) {
1922 if (stmt->label == label) {
1923 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1924 js_ReportCompileErrorNumber(cx, ts,
1925 JSREPORT_TS |
1926 JSREPORT_ERROR,
1927 JSMSG_BAD_CONTINUE);
1928 return NULL;
1929 }
1930 break;
1931 }
1932 } else {
1933 stmt2 = stmt;
1934 }
1935 }
1936 } else {
1937 for (; ; stmt = stmt->down) {
1938 if (!stmt) {
1939 js_ReportCompileErrorNumber(cx, ts,
1940 JSREPORT_TS | JSREPORT_ERROR,
1941 JSMSG_BAD_CONTINUE);
1942 return NULL;
1943 }
1944 if (STMT_IS_LOOP(stmt))
1945 break;
1946 }
1947 }
1948 if (label)
1949 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1950 break;
1952 case TOK_WITH:
1953 pn = NewParseNode(cx, ts, PN_BINARY, tc);
1954 if (!pn)
1955 return NULL;
1956 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1957 pn2 = Expr(cx, ts, tc);
1958 if (!pn2)
1959 return NULL;
1960 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1961 pn->pn_left = pn2;
1963 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1964 pn2 = Statement(cx, ts, tc);
1965 if (!pn2)
1966 return NULL;
1967 js_PopStatement(tc);
1969 pn->pn_pos.end = pn2->pn_pos.end;
1970 pn->pn_right = pn2;
1971 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1972 return pn;
1974 case TOK_VAR:
1975 pn = Variables(cx, ts, tc);
1976 if (!pn)
1977 return NULL;
1979 /* Tell js_EmitTree to generate a final POP. */
1980 pn->pn_extra |= PNX_POPVAR;
1981 break;
1983 case TOK_RETURN:
1984 if (!(tc->flags & TCF_IN_FUNCTION)) {
1985 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
1986 JSMSG_BAD_RETURN);
1987 return NULL;
1988 }
1989 pn = NewParseNode(cx, ts, PN_UNARY, tc);
1990 if (!pn)
1991 return NULL;
1993 /* This is ugly, but we don't want to require a semicolon. */
1994 ts->flags |= TSF_OPERAND;
1995 tt = js_PeekTokenSameLine(cx, ts);
1996 ts->flags &= ~TSF_OPERAND;
1997 if (tt == TOK_ERROR)
1998 return NULL;
2000 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2001 pn2 = Expr(cx, ts, tc);
2002 if (!pn2)
2003 return NULL;
2004 tc->flags |= TCF_RETURN_EXPR;
2005 pn->pn_pos.end = pn2->pn_pos.end;
2006 pn->pn_kid = pn2;
2007 } else {
2008 tc->flags |= TCF_RETURN_VOID;
2009 pn->pn_kid = NULL;
2010 }
2012 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 if (!ReportNoReturnValue(cx, ts))
2019 return NULL;
2020 }
2021 break;
2023 case TOK_LC:
2024 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
2025 pn = Statements(cx, ts, tc);
2026 if (!pn)
2027 return NULL;
2029 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
2030 js_PopStatement(tc);
2031 return pn;
2033 case TOK_EOL:
2034 case TOK_SEMI:
2035 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2036 if (!pn)
2037 return NULL;
2038 pn->pn_type = TOK_SEMI;
2039 pn->pn_kid = NULL;
2040 return pn;
2042 #if JS_HAS_DEBUGGER_KEYWORD
2043 case TOK_DEBUGGER:
2044 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
2045 if (!pn)
2046 return NULL;
2047 pn->pn_type = TOK_DEBUGGER;
2048 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2049 break;
2050 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2052 #if JS_HAS_XML_SUPPORT
2053 case TOK_DEFAULT:
2054 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2055 if (!pn)
2056 return NULL;
2057 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 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2064 JSMSG_BAD_DEFAULT_XML_NAMESPACE);
2065 return NULL;
2066 }
2067 pn2 = Expr(cx, ts, tc);
2068 if (!pn2)
2069 return NULL;
2070 pn->pn_op = JSOP_DEFXMLNS;
2071 pn->pn_pos.end = pn2->pn_pos.end;
2072 pn->pn_kid = pn2;
2073 tc->flags |= TCF_HAS_DEFXMLNS;
2074 break;
2075 #endif
2077 case TOK_ERROR:
2078 return NULL;
2080 default:
2081 #if JS_HAS_XML_SUPPORT
2082 expression:
2083 #endif
2084 js_UngetToken(ts);
2085 pn2 = Expr(cx, ts, tc);
2086 if (!pn2)
2087 return NULL;
2089 if (js_PeekToken(cx, ts) == TOK_COLON) {
2090 if (pn2->pn_type != TOK_NAME) {
2091 js_ReportCompileErrorNumber(cx, ts,
2092 JSREPORT_TS | JSREPORT_ERROR,
2093 JSMSG_BAD_LABEL);
2094 return NULL;
2095 }
2096 label = pn2->pn_atom;
2097 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
2098 if (stmt->type == STMT_LABEL && stmt->label == label) {
2099 js_ReportCompileErrorNumber(cx, ts,
2100 JSREPORT_TS | JSREPORT_ERROR,
2101 JSMSG_DUPLICATE_LABEL);
2102 return NULL;
2103 }
2104 }
2105 (void) js_GetToken(cx, ts);
2107 /* Push a label struct and parse the statement. */
2108 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
2109 stmtInfo.label = label;
2110 pn = Statement(cx, ts, tc);
2111 if (!pn)
2112 return NULL;
2114 /* Pop the label, set pn_expr, and return early. */
2115 js_PopStatement(tc);
2116 pn2->pn_type = TOK_COLON;
2117 pn2->pn_pos.end = pn->pn_pos.end;
2118 pn2->pn_expr = pn;
2119 return pn2;
2120 }
2122 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2123 if (!pn)
2124 return NULL;
2125 pn->pn_type = TOK_SEMI;
2126 pn->pn_pos = pn2->pn_pos;
2127 pn->pn_kid = pn2;
2128 break;
2129 }
2131 /* Check termination of this primitive statement. */
2132 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2133 tt = js_PeekTokenSameLine(cx, ts);
2134 if (tt == TOK_ERROR)
2135 return NULL;
2136 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2137 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2138 JSMSG_SEMI_BEFORE_STMNT);
2139 return NULL;
2140 }
2141 }
2143 (void) js_MatchToken(cx, ts, TOK_SEMI);
2144 return pn;
2145 }
2147 static JSParseNode *
2148 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2149 {
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;
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 pn = NewParseNode(cx, ts, PN_LIST, tc);
2173 if (!pn)
2174 return NULL;
2175 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2176 PN_INIT_LIST(pn);
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 for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
2191 continue;
2192 obj = fp->varobj;
2193 fun = fp->fun;
2194 clasp = OBJ_GET_CLASS(cx, obj);
2195 if (fun && clasp == &js_FunctionClass) {
2196 /* We are compiling code inside a function */
2197 getter = js_GetLocalVariable;
2198 setter = js_SetLocalVariable;
2199 } else if (fun && clasp == &js_CallClass) {
2200 /* We are compiling code from an eval inside a function */
2201 getter = js_GetCallVariable;
2202 setter = js_SetCallVariable;
2203 } else {
2204 getter = clasp->getProperty;
2205 setter = clasp->setProperty;
2206 }
2208 ok = JS_TRUE;
2209 do {
2210 currentGetter = getter;
2211 currentSetter = setter;
2212 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2213 atom = CURRENT_TOKEN(ts).t_atom;
2215 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2216 if (ale) {
2217 prevop = ALE_JSOP(ale);
2218 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 const char *name = js_AtomToPrintableString(cx, atom);
2222 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 return NULL;
2239 }
2240 }
2241 if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2242 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2243 } else {
2244 ale = js_IndexAtom(cx, atom, &tc->decls);
2245 if (!ale)
2246 return NULL;
2247 }
2248 ALE_SET_JSOP(ale, pn->pn_op);
2250 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2251 if (!pn2)
2252 return NULL;
2253 pn2->pn_op = JSOP_NAME;
2254 pn2->pn_atom = atom;
2255 pn2->pn_expr = NULL;
2256 pn2->pn_slot = -1;
2257 pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2258 ? JSPROP_PERMANENT | JSPROP_READONLY
2259 : JSPROP_PERMANENT;
2260 PN_APPEND(pn, pn2);
2262 if (!fun) {
2263 /* Don't lookup global variables at compile time. */
2264 prop = NULL;
2265 } else if (OBJ_IS_NATIVE(obj)) {
2266 if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
2267 &pobj, &prop)) {
2268 return NULL;
2269 }
2270 } else {
2271 if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
2272 return NULL;
2273 }
2274 if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2275 sprop = (JSScopeProperty *)prop;
2276 if (sprop->getter == js_GetArgument) {
2277 const char *name = js_AtomToPrintableString(cx, atom);
2278 if (!name) {
2279 ok = JS_FALSE;
2280 } else if (pn->pn_op == JSOP_DEFCONST) {
2281 js_ReportCompileErrorNumber(cx, ts,
2282 JSREPORT_TS | JSREPORT_ERROR,
2283 JSMSG_REDECLARED_PARAM,
2284 name);
2285 ok = JS_FALSE;
2286 } else {
2287 currentGetter = js_GetArgument;
2288 currentSetter = js_SetArgument;
2289 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 if (fun) {
2298 /* Not an argument, must be a redeclared local var. */
2299 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 } else if (clasp == &js_CallClass) {
2304 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 currentGetter = sprop->getter;
2319 currentSetter = sprop->setter;
2320 }
2321 }
2323 /* Override the old getter and setter, to handle eval. */
2324 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2325 0, sprop->attrs,
2326 currentGetter,
2327 currentSetter);
2328 if (!sprop)
2329 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 sprop = NULL;
2342 if (prop) {
2343 OBJ_DROP_PROPERTY(cx, pobj, prop);
2344 prop = NULL;
2345 }
2346 if (currentGetter == js_GetCallVariable) {
2347 /* Can't increase fun->nvars in an active frame! */
2348 currentGetter = clasp->getProperty;
2349 currentSetter = clasp->setProperty;
2350 }
2351 if (currentGetter == js_GetLocalVariable &&
2352 atom != cx->runtime->atomState.argumentsAtom &&
2353 fp->scopeChain == obj &&
2354 !js_InWithStatement(tc)) {
2355 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 return NULL;
2361 }
2362 if (fun->nvars == JS_BITMASK(16)) {
2363 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2364 JSMSG_TOO_MANY_FUN_VARS);
2365 return NULL;
2366 }
2367 fun->nvars++;
2368 }
2369 }
2371 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2372 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2373 js_ReportCompileErrorNumber(cx, ts,
2374 JSREPORT_TS | JSREPORT_ERROR,
2375 JSMSG_BAD_VAR_INIT);
2376 ok = JS_FALSE;
2377 } else {
2378 pn2->pn_expr = AssignExpr(cx, ts, tc);
2379 if (!pn2->pn_expr) {
2380 ok = JS_FALSE;
2381 } else {
2382 pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2383 ? JSOP_SETCONST
2384 : JSOP_SETNAME;
2385 if (atom == cx->runtime->atomState.argumentsAtom)
2386 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2387 }
2388 }
2389 }
2391 if (prop)
2392 OBJ_DROP_PROPERTY(cx, pobj, prop);
2393 if (!ok)
2394 return NULL;
2395 } while (js_MatchToken(cx, ts, TOK_COMMA));
2397 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2398 return pn;
2399 }
2401 static JSParseNode *
2402 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2403 {
2404 JSParseNode *pn, *pn2;
2406 pn = AssignExpr(cx, ts, tc);
2407 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2408 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
2409 if (!pn2)
2410 return NULL;
2411 pn2->pn_pos.begin = pn->pn_pos.begin;
2412 PN_INIT_LIST_1(pn2, pn);
2413 pn = pn2;
2414 do {
2415 pn2 = AssignExpr(cx, ts, tc);
2416 if (!pn2)
2417 return NULL;
2418 PN_APPEND(pn, pn2);
2419 } while (js_MatchToken(cx, ts, TOK_COMMA));
2420 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2421 }
2422 return pn;
2423 }
2425 static JSParseNode *
2426 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2427 {
2428 JSParseNode *pn, *pn2;
2429 JSTokenType tt;
2430 JSOp op;
2432 CHECK_RECURSION();
2434 pn = CondExpr(cx, ts, tc);
2435 if (!pn)
2436 return NULL;
2438 tt = js_GetToken(cx, ts);
2439 #if JS_HAS_GETTER_SETTER
2440 if (tt == TOK_NAME) {
2441 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2442 if (tt == TOK_ERROR)
2443 return NULL;
2444 }
2445 #endif
2446 if (tt != TOK_ASSIGN) {
2447 js_UngetToken(ts);
2448 return pn;
2449 }
2451 op = CURRENT_TOKEN(ts).t_op;
2452 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2453 continue;
2454 switch (pn2->pn_type) {
2455 case TOK_NAME:
2456 pn2->pn_op = JSOP_SETNAME;
2457 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2458 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2459 break;
2460 case TOK_DOT:
2461 pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD)
2462 ? JSOP_SETMETHOD
2463 : JSOP_SETPROP;
2464 break;
2465 case TOK_LB:
2466 pn2->pn_op = JSOP_SETELEM;
2467 break;
2468 #if JS_HAS_LVALUE_RETURN
2469 case TOK_LP:
2470 pn2->pn_op = JSOP_SETCALL;
2471 break;
2472 #endif
2473 #if JS_HAS_XML_SUPPORT
2474 case TOK_UNARYOP:
2475 if (pn2->pn_op == JSOP_XMLNAME) {
2476 pn2->pn_op = JSOP_SETXMLNAME;
2477 break;
2478 }
2479 /* FALL THROUGH */
2480 #endif
2481 default:
2482 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2483 JSMSG_BAD_LEFTSIDE_OF_ASS);
2484 return NULL;
2485 }
2486 pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2487 return pn;
2488 }
2490 static JSParseNode *
2491 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2492 {
2493 JSParseNode *pn, *pn1, *pn2, *pn3;
2494 #if JS_HAS_IN_OPERATOR
2495 uintN oldflags;
2496 #endif /* JS_HAS_IN_OPERATOR */
2498 pn = OrExpr(cx, ts, tc);
2499 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2500 pn1 = pn;
2501 pn = NewParseNode(cx, ts, PN_TERNARY, tc);
2502 if (!pn)
2503 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 oldflags = tc->flags;
2511 tc->flags &= ~TCF_IN_FOR_INIT;
2512 #endif /* JS_HAS_IN_OPERATOR */
2513 pn2 = AssignExpr(cx, ts, tc);
2514 #if JS_HAS_IN_OPERATOR
2515 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2516 #endif /* JS_HAS_IN_OPERATOR */
2518 if (!pn2)
2519 return NULL;
2520 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2521 pn3 = AssignExpr(cx, ts, tc);
2522 if (!pn3)
2523 return NULL;
2524 pn->pn_pos.begin = pn1->pn_pos.begin;
2525 pn->pn_pos.end = pn3->pn_pos.end;
2526 pn->pn_kid1 = pn1;
2527 pn->pn_kid2 = pn2;
2528 pn->pn_kid3 = pn3;
2529 }
2530 return pn;
2531 }
2533 static JSParseNode *
2534 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2535 {
2536 JSParseNode *pn;
2538 pn = AndExpr(cx, ts, tc);
2539 if (pn && js_MatchToken(cx, ts, TOK_OR))
2540 pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2541 return pn;
2542 }
2544 static JSParseNode *
2545 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2546 {
2547 JSParseNode *pn;
2549 pn = BitOrExpr(cx, ts, tc);
2550 if (pn && js_MatchToken(cx, ts, TOK_AND))
2551 pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2552 return pn;
2553 }
2555 static JSParseNode *
2556 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2557 {
2558 JSParseNode *pn;
2560 pn = BitXorExpr(cx, ts, tc);
2561 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2562 pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2563 tc);
2564 }
2565 return pn;
2566 }
2568 static JSParseNode *
2569 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2570 {
2571 JSParseNode *pn;
2573 pn = BitAndExpr(cx, ts, tc);
2574 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2575 pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2576 tc);
2577 }
2578 return pn;
2579 }
2581 static JSParseNode *
2582 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2583 {
2584 JSParseNode *pn;
2586 pn = EqExpr(cx, ts, tc);
2587 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2588 pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2589 return pn;
2590 }
2592 static JSParseNode *
2593 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2594 {
2595 JSParseNode *pn;
2596 JSOp op;
2598 pn = RelExpr(cx, ts, tc);
2599 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2600 op = CURRENT_TOKEN(ts).t_op;
2601 pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2602 }
2603 return pn;
2604 }
2606 static JSParseNode *
2607 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2608 {
2609 JSParseNode *pn;
2610 JSTokenType tt;
2611 JSOp op;
2612 #if JS_HAS_IN_OPERATOR
2613 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2615 /*
2616 * Uses of the in operator in ShiftExprs are always unambiguous,
2617 * so unset the flag that prohibits recognizing it.
2618 */
2619 tc->flags &= ~TCF_IN_FOR_INIT;
2620 #endif /* JS_HAS_IN_OPERATOR */
2622 pn = ShiftExpr(cx, ts, tc);
2623 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 tt = CURRENT_TOKEN(ts).type;
2637 op = CURRENT_TOKEN(ts).t_op;
2638 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 tc->flags |= inForInitFlag;
2643 #endif /* JS_HAS_IN_OPERATOR */
2645 return pn;
2646 }
2648 static JSParseNode *
2649 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2650 {
2651 JSParseNode *pn;
2652 JSOp op;
2654 pn = AddExpr(cx, ts, tc);
2655 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2656 op = CURRENT_TOKEN(ts).t_op;
2657 pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2658 }
2659 return pn;
2660 }
2662 static JSParseNode *
2663 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2664 {
2665 JSParseNode *pn;
2666 JSTokenType tt;
2667 JSOp op;
2669 pn = MulExpr(cx, ts, tc);
2670 while (pn &&
2671 (js_MatchToken(cx, ts, TOK_PLUS) ||
2672 js_MatchToken(cx, ts, TOK_MINUS))) {
2673 tt = CURRENT_TOKEN(ts).type;
2674 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2675 pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2676 }
2677 return pn;
2678 }
2680 static JSParseNode *
2681 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2682 {
2683 JSParseNode *pn;
2684 JSTokenType tt;
2685 JSOp op;
2687 pn = UnaryExpr(cx, ts, tc);
2688 while (pn &&
2689 (js_MatchToken(cx, ts, TOK_STAR) ||
2690 js_MatchToken(cx, ts, TOK_DIVOP))) {
2691 tt = CURRENT_TOKEN(ts).type;
2692 op = CURRENT_TOKEN(ts).t_op;
2693 pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2694 }
2695 return pn;
2696 }
2698 static JSParseNode *
2699 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2700 const char *name)
2701 {
2702 while (kid->pn_type == TOK_RP)
2703 kid = kid->pn_kid;
2704 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 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2714 JSMSG_BAD_OPERAND, name);
2715 return NULL;
2716 }
2717 pn->pn_kid = kid;
2718 return kid;
2719 }
2721 static const char *incop_name_str[] = {"increment", "decrement"};
2723 static JSBool
2724 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2725 JSParseNode *pn, JSParseNode *kid,
2726 JSTokenType tt, JSBool preorder)
2727 {
2728 JSOp op;
2730 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2731 if (!kid)
2732 return JS_FALSE;
2733 switch (kid->pn_type) {
2734 case TOK_NAME:
2735 op = (tt == TOK_INC)
2736 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2737 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2738 if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2739 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2740 break;
2742 case TOK_DOT:
2743 op = (tt == TOK_INC)
2744 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2745 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2746 break;
2748 #if JS_HAS_LVALUE_RETURN
2749 case TOK_LP:
2750 JS_ASSERT(kid->pn_op == JSOP_CALL);
2751 kid->pn_op = JSOP_SETCALL;
2752 /* FALL THROUGH */
2753 #endif
2754 #if JS_HAS_XML_SUPPORT
2755 case TOK_UNARYOP:
2756 if (kid->pn_op == JSOP_XMLNAME)
2757 kid->pn_op = JSOP_SETXMLNAME;
2758 /* FALL THROUGH */
2759 #endif
2760 case TOK_LB:
2761 op = (tt == TOK_INC)
2762 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2763 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2764 break;
2766 default:
2767 JS_ASSERT(0);
2768 op = JSOP_NOP;
2769 }
2770 pn->pn_op = op;
2771 return JS_TRUE;
2772 }
2774 static JSParseNode *
2775 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2776 {
2777 JSTokenType tt;
2778 JSParseNode *pn, *pn2;
2780 CHECK_RECURSION();
2782 ts->flags |= TSF_OPERAND;
2783 tt = js_GetToken(cx, ts);
2784 ts->flags &= ~TSF_OPERAND;
2786 switch (tt) {
2787 case TOK_UNARYOP:
2788 case TOK_PLUS:
2789 case TOK_MINUS:
2790 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2791 if (!pn)
2792 return NULL;
2793 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
2794 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2795 pn2 = UnaryExpr(cx, ts, tc);
2796 if (!pn2)
2797 return NULL;
2798 pn->pn_pos.end = pn2->pn_pos.end;
2799 pn->pn_kid = pn2;
2800 break;
2802 case TOK_INC:
2803 case TOK_DEC:
2804 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2805 if (!pn)
2806 return NULL;
2807 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2808 if (!pn2)
2809 return NULL;
2810 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2811 return NULL;
2812 pn->pn_pos.end = pn2->pn_pos.end;
2813 break;
2815 case TOK_DELETE:
2816 pn = NewParseNode(cx, ts, PN_UNARY, tc);
2817 if (!pn)
2818 return NULL;
2819 pn2 = UnaryExpr(cx, ts, tc);
2820 if (!pn2)
2821 return NULL;
2822 pn->pn_pos.end = pn2->pn_pos.end;
2824 /*
2825 * Under ECMA3, deleting any unary expression is valid -- it simply
2826 * returns true. Here we strip off any parentheses.
2827 */
2828 while (pn2->pn_type == TOK_RP)
2829 pn2 = pn2->pn_kid;
2830 pn->pn_kid = pn2;
2831 break;
2833 case TOK_ERROR:
2834 return NULL;
2836 default:
2837 js_UngetToken(ts);
2838 pn = MemberExpr(cx, ts, tc, JS_TRUE);
2839 if (!pn)
2840 return NULL;
2842 /* Don't look across a newline boundary for a postfix incop. */
2843 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2844 tt = js_PeekTokenSameLine(cx, ts);
2845 if (tt == TOK_INC || tt == TOK_DEC) {
2846 (void) js_GetToken(cx, ts);
2847 pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
2848 if (!pn2)
2849 return NULL;
2850 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2851 return NULL;
2852 pn2->pn_pos.begin = pn->pn_pos.begin;
2853 pn = pn2;
2854 }
2855 }
2856 break;
2857 }
2858 return pn;
2859 }
2861 static JSBool
2862 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2863 JSParseNode *listNode)
2864 {
2865 JSBool matched;
2867 ts->flags |= TSF_OPERAND;
2868 matched = js_MatchToken(cx, ts, TOK_RP);
2869 ts->flags &= ~TSF_OPERAND;
2870 if (!matched) {
2871 do {
2872 JSParseNode *argNode = AssignExpr(cx, ts, tc);
2873 if (!argNode)
2874 return JS_FALSE;
2875 PN_APPEND(listNode, argNode);
2876 } while (js_MatchToken(cx, ts, TOK_COMMA));
2878 if (js_GetToken(cx, ts) != TOK_RP) {
2879 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
2880 JSMSG_PAREN_AFTER_ARGS);
2881 return JS_FALSE;
2882 }
2883 }
2884 return JS_TRUE;
2885 }
2887 static JSParseNode *
2888 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2889 JSBool allowCallSyntax)
2890 {
2891 JSParseNode *pn, *pn2, *pn3;
2892 JSTokenType tt;
2894 CHECK_RECURSION();
2896 /* Check for new expression first. */
2897 ts->flags |= TSF_OPERAND;
2898 tt = js_PeekToken(cx, ts);
2899 ts->flags &= ~TSF_OPERAND;
2900 if (tt == TOK_NEW) {
2901 (void) js_GetToken(cx, ts);
2903 pn = NewParseNode(cx, ts, PN_LIST, tc);
2904 if (!pn)
2905 return NULL;
2906 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2907 if (!pn2)
2908 return NULL;
2909 pn->pn_op = JSOP_NEW;
2910 PN_INIT_LIST_1(pn, pn2);
2911 pn->pn_pos.begin = pn2->pn_pos.begin;
2913 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2914 return NULL;
2915 if (pn->pn_count > ARGC_LIMIT) {
2916 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2917 JSMSG_TOO_MANY_CON_ARGS);
2918 return NULL;
2919 }
2920 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2921 } else {
2922 pn = PrimaryExpr(cx, ts, tc);
2923 if (!pn)
2924 return NULL;
2926 if (pn->pn_type == TOK_ANYNAME ||
2927 pn->pn_type == TOK_AT ||
2928 pn->pn_type == TOK_DBLCOLON) {
2929 pn2 = NewOrRecycledNode(cx, tc);
2930 if (!pn2)
2931 return NULL;
2932 pn2->pn_type = TOK_UNARYOP;
2933 pn2->pn_pos = pn->pn_pos;
2934 pn2->pn_op = JSOP_XMLNAME;
2935 pn2->pn_arity = PN_UNARY;
2936 pn2->pn_kid = pn;
2937 pn2->pn_next = NULL;
2938 #if JS_HAS_XML_SUPPORT
2939 pn2->pn_ts = ts;
2940 #endif
2941 pn = pn2;
2942 }
2943 }
2945 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2946 if (tt == TOK_DOT) {
2947 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
2948 if (!pn2)
2949 return NULL;
2950 #if JS_HAS_XML_SUPPORT
2951 pn3 = PrimaryExpr(cx, ts, tc);
2952 if (!pn3)
2953 return NULL;
2954 tt = pn3->pn_type;
2955 if (tt == TOK_NAME ||
2956 (tt == TOK_DBLCOLON &&
2957 pn3->pn_arity == PN_NAME &&
2958 pn3->pn_expr->pn_type == TOK_FUNCTION)) {
2959 pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD;
2960 pn2->pn_expr = pn;
2961 pn2->pn_atom = pn3->pn_atom;
2962 RecycleTree(pn3, tc);
2963 } else {
2964 if (TOKEN_TYPE_IS_XML(tt)) {
2965 pn2->pn_type = TOK_LB;
2966 pn2->pn_op = JSOP_GETELEM;
2967 } else if (tt == TOK_RP) {
2968 JSParseNode *group = pn3;
2970 /* Recycle the useless TOK_RP/JSOP_GROUP node. */
2971 pn3 = group->pn_kid;
2972 group->pn_kid = NULL;
2973 RecycleTree(group, tc);
2974 pn2->pn_type = TOK_FILTER;
2975 pn2->pn_op = JSOP_FILTER;
2976 } else {
2977 js_ReportCompileErrorNumber(cx, ts,
2978 JSREPORT_TS | JSREPORT_ERROR,
2979 JSMSG_NAME_AFTER_DOT);
2980 return NULL;
2981 }
2982 pn2->pn_arity = PN_BINARY;
2983 pn2->pn_left = pn;
2984 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 pn2->pn_pos.begin = pn->pn_pos.begin;
2993 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2994 #if JS_HAS_XML_SUPPORT
2995 } else if (tt == TOK_DBLDOT) {
2996 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
2997 if (!pn2)
2998 return NULL;
2999 pn3 = PrimaryExpr(cx, ts, tc);
3000 if (!pn3)
3001 return NULL;
3002 tt = pn3->pn_type;
3003 if (tt == TOK_NAME) {
3004 pn3->pn_type = TOK_STRING;
3005 pn3->pn_arity = PN_NULLARY;
3006 pn3->pn_op = JSOP_STRING;
3007 } else if (!TOKEN_TYPE_IS_XML(tt)) {
3008 js_ReportCompileErrorNumber(cx, ts,
3009 JSREPORT_TS | JSREPORT_ERROR,
3010 JSMSG_NAME_AFTER_DOT);
3011 return NULL;
3012 }
3013 pn2->pn_op = JSOP_DESCENDANTS;
3014 pn2->pn_left = pn;
3015 pn2->pn_right = pn3;
3016 pn2->pn_pos.begin = pn->pn_pos.begin;
3017 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3018 #endif
3019 } else if (tt == TOK_LB) {
3020 pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
3021 if (!pn2)
3022 return NULL;
3023 pn3 = Expr(cx, ts, tc);
3024 if (!pn3)
3025 return NULL;
3027 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
3028 pn2->pn_pos.begin = pn->pn_pos.begin;
3029 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3031 /* Optimize o['p'] to o.p by rewriting pn2. */
3032 if (pn3->pn_type == TOK_STRING) {
3033 pn2->pn_type = TOK_DOT;
3034 pn2->pn_op = JSOP_GETPROP;
3035 pn2->pn_arity = PN_NAME;
3036 pn2->pn_expr = pn;
3037 pn2->pn_atom = pn3->pn_atom;
3038 } else {
3039 pn2->pn_op = JSOP_GETELEM;
3040 pn2->pn_left = pn;
3041 pn2->pn_right = pn3;
3042 }
3043 } else if (allowCallSyntax && tt == TOK_LP) {
3044 pn2 = NewParseNode(cx, ts, PN_LIST, tc);
3045 if (!pn2)
3046 return NULL;
3048 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
3049 pn2->pn_op = JSOP_CALL;
3050 if (pn->pn_op == JSOP_NAME &&
3051 pn->pn_atom == cx->runtime->atomState.evalAtom) {
3052 pn2->pn_op = JSOP_EVAL;
3053 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3054 }
3056 PN_INIT_LIST_1(pn2, pn);
3057 pn2->pn_pos.begin = pn->pn_pos.begin;
3059 if (!ArgumentList(cx, ts, tc, pn2))
3060 return NULL;
3061 if (pn2->pn_count > ARGC_LIMIT) {
3062 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3063 JSMSG_TOO_MANY_FUN_ARGS);
3064 return NULL;
3065 }
3066 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3067 } else {
3068 js_UngetToken(ts);
3069 return pn;
3070 }
3072 pn = pn2;
3073 }
3074 if (tt == TOK_ERROR)
3075 return NULL;
3076 return pn;
3077 }
3079 static JSParseNode *
3080 BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3081 {
3082 uintN oldflags;
3083 JSParseNode *pn;
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 oldflags = tc->flags;
3092 tc->flags &= ~TCF_IN_FOR_INIT;
3093 #endif
3094 pn = Expr(cx, ts, tc);
3095 #if JS_HAS_IN_OPERATOR
3096 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3097 #endif
3098 return pn;
3099 }
3101 #if JS_HAS_XML_SUPPORT
3103 static JSParseNode *
3104 EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3105 {
3106 JSParseNode *pn;
3108 pn = BracketedExpr(cx, ts, tc);
3109 if (!pn)
3110 return NULL;
3112 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR);
3113 return pn;
3114 }
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 {
3170 JSParseNode *pn;
3172 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3173 if (!pn)
3174 return NULL;
3175 if (pn->pn_type == TOK_STAR) {
3176 pn->pn_type = TOK_ANYNAME;
3177 pn->pn_op = JSOP_ANYNAME;
3178 pn->pn_atom = cx->runtime->atomState.starAtom;
3179 } else {
3180 JS_ASSERT(pn->pn_type == TOK_NAME);
3181 pn->pn_op = JSOP_QNAMEPART;
3182 pn->pn_arity = PN_NAME;
3183 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3184 pn->pn_expr = NULL;
3185 pn->pn_slot = -1;
3186 pn->pn_attrs = 0;
3187 }
3188 return pn;
3189 }
3191 static JSParseNode *
3192 QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
3193 JSTreeContext *tc)
3194 {
3195 JSParseNode *pn2, *pn3;
3196 JSTokenType tt;
3198 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON);
3199 pn2 = NewParseNode(cx, ts, PN_NAME, tc);
3200 if (!pn2)
3201 return NULL;
3203 /* Left operand of :: must be evaluated if it is an identifier. */
3204 if (pn->pn_op == JSOP_QNAMEPART)
3205 pn->pn_op = JSOP_NAME;
3207 tt = js_GetToken(cx, ts);
3208 if (tt == TOK_STAR || tt == TOK_NAME) {
3209 /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
3210 pn2->pn_op = JSOP_QNAMECONST;
3211 pn2->pn_atom = (tt == TOK_STAR)
3212 ? cx->runtime->atomState.starAtom
3213 : CURRENT_TOKEN(ts).t_atom;
3214 pn2->pn_expr = pn;
3215 pn2->pn_slot = -1;
3216 pn2->pn_attrs = 0;
3217 return pn2;
3218 }
3220 if (tt != TOK_LB) {
3221 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3222 JSMSG_SYNTAX_ERROR);
3223 return NULL;
3224 }
3225 pn3 = EndBracketedExpr(cx, ts, tc);
3226 if (!pn3)
3227 return NULL;
3229 pn2->pn_op = JSOP_QNAME;
3230 pn2->pn_arity = PN_BINARY;
3231 pn2->pn_left = pn;
3232 pn2->pn_right = pn3;
3233 return pn2;
3234 }
3236 static JSParseNode *
3237 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3238 {
3239 JSParseNode *pn;
3241 pn = PropertySelector(cx, ts, tc);
3242 if (!pn)
3243 return NULL;
3244 if (js_MatchToken(cx, ts, TOK_DBLCOLON))
3245 pn = QualifiedSuffix(cx, ts, pn, tc);
3246 return pn;
3247 }
3249 static JSParseNode *
3250 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3251 {
3252 JSParseNode *pn, *pn2;
3253 JSTokenType tt;
3255 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT);
3256 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3257 if (!pn)
3258 return NULL;
3259 pn->pn_op = JSOP_TOATTRNAME;
3260 tt = js_GetToken(cx, ts);
3261 if (tt == TOK_STAR || tt == TOK_NAME) {
3262 pn2 = QualifiedIdentifier(cx, ts, tc);
3263 } else if (tt == TOK_LB) {
3264 pn2 = EndBracketedExpr(cx, ts, tc);
3265 } else {
3266 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3267 JSMSG_SYNTAX_ERROR);
3268 return NULL;
3269 }
3270 if (!pn2)
3271 return NULL;
3272 pn->pn_kid = pn2;
3273 return pn;
3274 }
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 {
3282 JSParseNode *pn, *pn2;
3283 uintN oldflags;
3285 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC);
3286 pn = NewParseNode(cx, ts, PN_UNARY, tc);
3287 if (!pn)
3288 return NULL;
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 oldflags = ts->flags;
3297 ts->flags = oldflags & ~TSF_XMLTAGMODE;
3298 pn2 = Expr(cx, ts, tc);
3299 if (!pn2)
3300 return NULL;
3302 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
3303 ts->flags = oldflags;
3304 pn->pn_kid = pn2;
3305 pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR;
3306 return pn;
3307 }
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 {
3318 JSParseNode *pn;
3319 JSToken *tp;
3321 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
3322 if (!pn)
3323 return NULL;
3324 tp = &CURRENT_TOKEN(ts);
3325 pn->pn_op = tp->t_op;
3326 pn->pn_atom = tp->t_atom;
3327 if (tp->type == TOK_XMLPI)
3328 pn->pn_atom2 = tp->t_atom2;
3329 return pn;
3330 }
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 {
3347 JSParseNode *pn, *pn2, *list;
3348 JSTokenType tt;
3350 pn = list = NULL;
3351 do {
3352 tt = CURRENT_TOKEN(ts).type;
3353 if (tt == TOK_LC) {
3354 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
3355 if (!pn2)
3356 return NULL;
3357 } else {
3358 JS_ASSERT(tt == TOK_XMLNAME);
3359 pn2 = XMLAtomNode(cx, ts, tc);
3360 if (!pn2)
3361 return NULL;
3362 }
3364 if (!pn) {
3365 pn = pn2;
3366 } else {
3367 if (!list) {
3368 list = NewParseNode(cx, ts, PN_LIST, tc);
3369 if (!list)
3370 return NULL;
3371 list->pn_type = TOK_XMLNAME;
3372 list->pn_pos.begin = pn->pn_pos.begin;
3373 PN_INIT_LIST_1(list, pn);
3374 list->pn_extra = PNX_CANTFOLD;
3375 pn = list;
3376 }
3377 pn->pn_pos.end = pn2->pn_pos.end;
3378 PN_APPEND(pn, pn2);
3379 }
3380 } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC);
3382 js_UngetToken(ts);
3383 return pn;
3384 }
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)
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 {
3415 JSParseNode *pn, *pn2, *list;
3416 JSTokenType tt;
3418 pn = XMLNameExpr(cx, ts, tc);
3419 if (!pn)
3420 return NULL;
3421 *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL;
3422 list = NULL;
3424 while (js_MatchToken(cx, ts, TOK_XMLSPACE)) {
3425 tt = js_GetToken(cx, ts);
3426 if (tt != TOK_XMLNAME && tt != TOK_LC) {
3427 js_UngetToken(ts);
3428 break;
3429 }
3431 pn2 = XMLNameExpr(cx, ts, tc);
3432 if (!pn2)
3433 return NULL;
3434 if (!list) {
3435 list = NewParseNode(cx, ts, PN_LIST, tc);
3436 if (!list)
3437 return NULL;
3438 list->pn_type = tagtype;
3439 list->pn_pos.begin = pn->pn_pos.begin;
3440 PN_INIT_LIST_1(list, pn);
3441 pn = list;
3442 }
3443 PN_APPEND(pn, pn2);
3444 if (!XML_FOLDABLE(pn2))
3445 pn->pn_extra |= PNX_CANTFOLD;
3447 js_MatchToken(cx, ts, TOK_XMLSPACE);
3448 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR);
3449 js_MatchToken(cx, ts, TOK_XMLSPACE);
3451 tt = js_GetToken(cx, ts);
3452 if (tt == TOK_XMLATTR) {
3453 pn2 = XMLAtomNode(cx, ts, tc);
3454 } else if (tt == TOK_LC) {
3455 pn2 = XMLExpr(cx, ts, JS_TRUE, tc);
3456 pn->pn_extra |= PNX_CANTFOLD;
3457 } else {
3458 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3459 JSMSG_BAD_XML_ATTR_VALUE);
3460 return NULL;
3461 }
3462 if (!pn2)
3463 return NULL;
3464 pn->pn_pos.end = pn2->pn_pos.end;
3465 PN_APPEND(pn, pn2);
3466 }
3468 return pn;
3469 }
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
3483 static JSParseNode *
3484 XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3485 JSBool allowList);
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 {
3495 JSTokenType tt;
3496 JSParseNode *pn2;
3497 JSAtom *textAtom;
3499 ts->flags &= ~TSF_XMLTAGMODE;
3500 for (;;) {
3501 ts->flags |= TSF_XMLTEXTMODE;
3502 tt = js_GetToken(cx, ts);
3503 ts->flags &= ~TSF_XMLTEXTMODE;
3504 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
3506 JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT);
3507 textAtom = CURRENT_TOKEN(ts).t_atom;
3508 if (textAtom) {
3509 /* Non-zero-length XML text scanned. */
3510 pn2 = XMLAtomNode(cx, ts, tc);
3511 if (!pn2)
3512 return JS_FALSE;
3513 pn->pn_pos.end = pn2->pn_pos.end;
3514 PN_APPEND(pn, pn2);
3515 }
3517 ts->flags |= TSF_OPERAND;
3518 tt = js_GetToken(cx, ts);
3519 ts->flags &= ~TSF_OPERAND;
3520 XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE);
3521 if (tt == TOK_XMLETAGO)
3522 break;
3524 if (tt == TOK_LC) {
3525 pn2 = XMLExpr(cx, ts, JS_FALSE, tc);
3526 pn->pn_extra |= PNX_CANTFOLD;
3527 } else if (tt == TOK_XMLSTAGO) {
3528 pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE);
3529 if (pn2) {
3530 pn2->pn_extra &= ~PNX_XMLROOT;
3531 pn->pn_extra |= pn2->pn_extra;
3532 }
3533 } else {
3534 JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT ||
3535 tt == TOK_XMLPI);
3536 pn2 = XMLAtomNode(cx, ts, tc);
3537 }
3538 if (!pn2)
3539 return JS_FALSE;
3540 pn->pn_pos.end = pn2->pn_pos.end;
3541 PN_APPEND(pn, pn2);
3542 }
3544 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO);
3545 ts->flags |= TSF_XMLTAGMODE;
3546 return JS_TRUE;
3547 }
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 {
3556 JSParseNode *pn, *pn2, *list;
3557 JSBool hadSpace;
3558 JSTokenType tt;
3559 JSAtom *startAtom, *endAtom;
3561 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO);
3562 pn = NewParseNode(cx, ts, PN_LIST, tc);
3563 if (!pn)
3564 return NULL;
3566 ts->flags |= TSF_XMLTAGMODE;
3567 hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE);
3568 tt = js_GetToken(cx, ts);
3569 if (tt == TOK_ERROR)
3570 return NULL;
3572 if (tt == TOK_XMLNAME || tt == TOK_LC) {
3573 /*
3574 * XMLElement. Append the tag and its contents, if any, to pn.
3575 */
3576 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom);
3577 if (!pn2)
3578 return NULL;
3579 js_MatchToken(cx, ts, TOK_XMLSPACE);
3581 tt = js_GetToken(cx, ts);
3582 if (tt == TOK_XMLPTAGC) {
3583 /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */
3584 if (pn2->pn_type == TOK_XMLSTAGO) {
3585 PN_INIT_LIST(pn);
3586 RecycleTree(pn, tc);
3587 pn = pn2;
3588 } else {
3589 JS_ASSERT(pn2->pn_type == TOK_XMLNAME ||
3590 pn2->pn_type == TOK_LC);
3591 PN_INIT_LIST_1(pn, pn2);
3592 if (!XML_FOLDABLE(pn2))
3593 pn->pn_extra |= PNX_CANTFOLD;
3594 }
3595 pn->pn_type = TOK_XMLPTAGC;
3596 pn->pn_extra |= PNX_XMLROOT;
3597 } else {
3598 /* We had better have a tag-close (>) at this point. */
3599 if (tt != TOK_XMLTAGC) {
3600 js_ReportCompileErrorNumber(cx, ts,
3601 JSREPORT_TS | JSREPORT_ERROR,
3602 JSMSG_BAD_XML_TAG_SYNTAX);
3603 return NULL;
3604 }
3605 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3607 /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */
3608 if (pn2->pn_type != TOK_XMLSTAGO) {
3609 PN_INIT_LIST_1(pn, pn2);
3610 if (!XML_FOLDABLE(pn2))
3611 pn->pn_extra |= PNX_CANTFOLD;
3612 pn2 = pn;
3613 pn = NewParseNode(cx, ts, PN_LIST, tc);
3614 if (!pn)
3615 return NULL;
3616 }
3618 /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
3619 pn->pn_type = TOK_XMLELEM;
3620 PN_INIT_LIST_1(pn, pn2);
3621 if (!XML_FOLDABLE(pn2))
3622 pn->pn_extra |= PNX_CANTFOLD;
3623 pn->pn_extra |= PNX_XMLROOT;
3625 /* Get element contents and delimiting end-tag-open sequence. */
3626 if (!XMLElementContent(cx, ts, pn, tc))
3627 return NULL;
3629 js_MatchToken(cx, ts, TOK_XMLSPACE);
3630 tt = js_GetToken(cx, ts);
3631 XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL);
3632 if (tt != TOK_XMLNAME && tt != TOK_LC) {
3633 js_ReportCompileErrorNumber(cx, ts,
3634 JSREPORT_TS | JSREPORT_ERROR,
3635 JSMSG_BAD_XML_TAG_SYNTAX);
3636 return NULL;
3637 }
3639 /* Parse end tag; check mismatch at compile-time if we can. */
3640 pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom);
3641 if (!pn2)
3642 return NULL;
3643 if (pn2->pn_type == TOK_XMLETAGO) {
3644 /* Oops, end tag has attributes! */
3645 js_ReportCompileErrorNumber(cx, ts,
3646 JSREPORT_TS | JSREPORT_ERROR,
3647 JSMSG_BAD_XML_TAG_SYNTAX);
3648 return NULL;
3649 }
3650 if (endAtom && startAtom && endAtom != startAtom) {
3651 /* End vs. start tag name mismatch: point to the tag name. */
3652 ++pn2->pn_pos.begin.index;
3653 js_ReportCompileErrorNumber(cx, pn2,
3654 JSREPORT_PN | JSREPORT_ERROR,
3655 JSMSG_XML_TAG_NAME_MISMATCH);
3656 return NULL;
3657 }
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 list = NewParseNode(cx, ts, PN_LIST, tc);
3662 if (!list)
3663 return NULL;
3664 list->pn_type = TOK_XMLETAGO;
3665 PN_INIT_LIST_1(list, pn2);
3666 PN_APPEND(pn, list);
3667 if (!XML_FOLDABLE(pn2)) {
3668 list->pn_extra |= PNX_CANTFOLD;
3669 pn->pn_extra |= PNX_CANTFOLD;
3670 }
3672 js_MatchToken(cx, ts, TOK_XMLSPACE);
3673 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX);
3674 }
3676 /* Set pn_op now that pn has been updated to its final value. */
3677 pn->pn_op = JSOP_TOXML;
3678 } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) {
3679 /* XMLList Initialiser. */
3680 pn->pn_type = TOK_XMLLIST;
3681 pn->pn_op = JSOP_TOXMLLIST;
3682 PN_INIT_LIST(pn);
3683 pn->pn_extra |= PNX_XMLROOT;
3684 if (!XMLElementContent(cx, ts, pn, tc))
3685 return NULL;
3687 MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX);
3688 } else {
3689 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3690 JSMSG_BAD_XML_NAME_SYNTAX);
3691 return NULL;
3692 }
3694 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3695 ts->flags &= ~TSF_XMLTAGMODE;
3696 return pn;
3697 }
3699 static JSParseNode *
3700 XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3701 JSBool allowList)
3702 {
3703 uint32 oldopts;
3704 JSParseNode *pn;
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 oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML);
3713 pn = XMLElementOrList(cx, ts, tc, allowList);
3714 JS_SetOptions(cx, oldopts);
3715 return pn;
3716 }
3718 JS_FRIEND_API(JSParseNode *)
3719 js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
3720 JSBool allowList)
3721 {
3722 JSStackFrame *fp, frame;
3723 JSParseNode *pn;
3724 JSTreeContext tc;
3725 JSTokenType tt;
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 fp = cx->fp;
3733 if (!fp || !fp->varobj || fp->scopeChain != chain) {
3734 memset(&frame, 0, sizeof frame);
3735 frame.varobj = frame.scopeChain = chain;
3736 if (cx->options & JSOPTION_VAROBJFIX) {
3737 while ((chain = JS_GetParent(cx, chain)) != NULL)
3738 frame.varobj = chain;
3739 }
3740 frame.down = fp;
3741 if (fp) {
3742 frame.flags = fp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO |
3743 JSFRAME_SCRIPT_OBJECT);
3744 }
3745 cx->fp = &frame;
3746 }
3748 JS_KEEP_ATOMS(cx->runtime);
3749 TREE_CONTEXT_INIT(&tc);
3751 /* Set XML-only mode to turn off special treatment of {expr} in XML. */
3752 ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
3753 tt = js_GetToken(cx, ts);
3754 ts->flags &= ~TSF_OPERAND;
3756 if (tt != TOK_XMLSTAGO) {
3757 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
3758 JSMSG_BAD_XML_MARKUP);
3759 pn = NULL;
3760 } else {
3761 pn = XMLElementOrListRoot(cx, ts, &tc, allowList);
3762 }
3764 ts->flags &= ~TSF_XMLONLYMODE;
3765 TREE_CONTEXT_FINISH(&tc);
3766 JS_UNKEEP_ATOMS(cx->runtime);
3767 cx->fp = fp;
3768 return pn;
3769 }
3771 #endif /* JS_HAS_XMLSUPPORT */
3773 static JSParseNode *
3774 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3775 {
3776 JSTokenType tt;
3777 JSParseNode *pn, *pn2, *pn3;
3778 #if JS_HAS_GETTER_SETTER
3779 JSAtom *atom;
3780 JSRuntime *rt;
3781 #endif
3783 #if JS_HAS_SHARP_VARS
3784 JSParseNode *defsharp;
3785 JSBool notsharp;
3787 defsharp = NULL;
3788 notsharp = JS_FALSE;
3789 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
3797 CHECK_RECURSION();
3799 ts->flags |= TSF_OPERAND;
3800 tt = js_GetToken(cx, ts);
3801 ts->flags &= ~TSF_OPERAND;
3803 #if JS_HAS_GETTER_SETTER
3804 if (tt == TOK_NAME) {
3805 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
3806 if (tt == TOK_ERROR)
3807 return NULL;
3808 }
3809 #endif
3811 switch (tt) {
3812 #if JS_HAS_LEXICAL_CLOSURE || JS_HAS_XML_SUPPORT
3813 case TOK_FUNCTION:
3814 #if JS_HAS_XML_SUPPORT
3815 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
3816 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
3817 if (!pn2)
3818 return NULL;
3819 pn2->pn_type = TOK_FUNCTION;
3820 pn = QualifiedSuffix(cx, ts, pn2, tc);
3821 if (!pn)
3822 return NULL;
3823 break;
3824 }
3825 #endif
3826 pn = FunctionExpr(cx, ts, tc);
3827 if (!pn)
3828 return NULL;
3829 break;
3830 #endif
3832 #if JS_HAS_INITIALIZERS
3833 case TOK_LB:
3834 {
3835 JSBool matched;
3836 jsuint atomIndex;
3838 pn = NewParseNode(cx, ts, PN_LIST, tc);
3839 if (!pn)
3840 return NULL;
3841 pn->pn_type = TOK_RB;
3843 #if JS_HAS_SHARP_VARS
3844 if (defsharp) {
3845 PN_INIT_LIST_1(pn, defsharp);
3846 defsharp = NULL;
3847 } else
3848 #endif
3849 PN_INIT_LIST(pn);
3851 ts->flags |= TSF_OPERAND;
3852 matched = js_MatchToken(cx, ts, TOK_RB);
3853 ts->flags &= ~TSF_OPERAND;
3854 if (!matched) {
3855 for (atomIndex = 0; ; atomIndex++) {
3856 if (atomIndex == ATOM_INDEX_LIMIT) {
3857 js_ReportCompileErrorNumber(cx, ts,
3858 JSREPORT_TS | JSREPORT_ERROR,
3859 JSMSG_ARRAY_INIT_TOO_BIG);
3860 return NULL;
3861 }
3863 ts->flags |= TSF_OPERAND;
3864 tt = js_PeekToken(cx, ts);
3865 ts->flags &= ~TSF_OPERAND;
3866 if (tt == TOK_RB) {
3867 pn->pn_extra |= PNX_ENDCOMMA;
3868 break;
3869 }
3871 if (tt == TOK_COMMA) {
3872 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
3873 js_MatchToken(cx, ts, TOK_COMMA);
3874 pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
3875 } else {
3876 pn2 = AssignExpr(cx, ts, tc);
3877 }
3878 if (!pn2)
3879 return NULL;
3880 PN_APPEND(pn, pn2);
3882 if (tt != TOK_COMMA) {
3883 /* If we didn't already match TOK_COMMA in above case. */
3884 if (!js_MatchToken(cx, ts, TOK_COMMA))
3885 break;
3886 }
3887 }
3889 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
3890 }
3891 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3892 return pn;
3893 }
3895 case TOK_LC:
3896 pn = NewParseNode(cx, ts, PN_LIST, tc);
3897 if (!pn)
3898 return NULL;
3899 pn->pn_type = TOK_RC;
3901 #if JS_HAS_SHARP_VARS
3902 if (defsharp) {
3903 PN_INIT_LIST_1(pn, defsharp);
3904 defsharp = NULL;
3905 } else
3906 #endif
3907 PN_INIT_LIST(pn);
3909 if (!js_MatchToken(cx, ts, TOK_RC)) {
3910 do {
3911 JSOp op;
3913 tt = js_GetToken(cx, ts);
3914 switch (tt) {
3915 case TOK_NUMBER:
3916 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
3917 if (pn3)
3918 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
3919 break;
3920 case TOK_NAME:
3921 #if JS_HAS_GETTER_SETTER
3922 atom = CURRENT_TOKEN(ts).t_atom;
3923 rt = cx->runtime;
3924 if (atom == rt->atomState.getAtom ||
3925 atom == rt->atomState.setAtom) {
3926 op = (atom == rt->atomState.getAtom)
3927 ? JSOP_GETTER
3928 : JSOP_SETTER;
3929 if (js_MatchToken(cx, ts, TOK_NAME)) {
3930 pn3 = NewParseNode(cx, ts, PN_NAME, tc);
3931 if (!pn3)
3932 return NULL;
3933 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3934 pn3->pn_expr = NULL;
3936 /* We have to fake a 'function' token here. */
3937 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
3938 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
3939 pn2 = FunctionExpr(cx, ts, tc);
3940 pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
3941 goto skip;
3942 }
3943 }
3944 /* else fall thru ... */
3945 #endif
3946 case TOK_STRING:
3947 pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
3948 if (pn3)
3949 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3950 break;
3951 case TOK_RC:
3952 if (!js_ReportCompileErrorNumber(cx, ts,
3953 JSREPORT_TS |
3954 JSREPORT_WARNING |
3955 JSREPORT_STRICT,
3956 JSMSG_TRAILING_COMMA)) {
3957 return NULL;
3958 }
3959 goto end_obj_init;
3960 default:
3961 js_ReportCompileErrorNumber(cx, ts,
3962 JSREPORT_TS | JSREPORT_ERROR,
3963 JSMSG_BAD_PROP_ID);
3964 return NULL;
3965 }
3967 tt = js_GetToken(cx, ts);
3968 #if JS_HAS_GETTER_SETTER
3969 if (tt == TOK_NAME) {
3970 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
3971 if (tt == TOK_ERROR)
3972 return NULL;
3973 }
3974 #endif
3975 if (tt != TOK_COLON) {
3976 js_ReportCompileErrorNumber(cx, ts,
3977 JSREPORT_TS | JSREPORT_ERROR,
3978 JSMSG_COLON_AFTER_ID);
3979 return NULL;
3980 }
3981 op = CURRENT_TOKEN(ts).t_op;
3982 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
3983 tc);
3984 #if JS_HAS_GETTER_SETTER
3985 skip:
3986 #endif
3987 if (!pn2)
3988 return NULL;
3989 PN_APPEND(pn, pn2);
3990 } while (js_MatchToken(cx, ts, TOK_COMMA));
3992 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
3993 }
3994 end_obj_init:
3995 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3996 return pn;
3998 #if JS_HAS_SHARP_VARS
3999 case TOK_DEFSHARP:
4000 if (defsharp)
4001 goto badsharp;
4002 defsharp = NewParseNode(cx, ts, PN_UNARY, tc);
4003 if (!defsharp)
4004 return NULL;
4005 defsharp->pn_kid = NULL;
4006 defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
4007 goto again;
4009 case TOK_USESHARP:
4010 /* Check for forward/dangling references at runtime, to allow eval. */
4011 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4012 if (!pn)
4013 return NULL;
4014 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
4015 notsharp = JS_TRUE;
4016 break;
4017 #endif /* JS_HAS_SHARP_VARS */
4018 #endif /* JS_HAS_INITIALIZERS */
4020 case TOK_LP:
4021 pn = NewParseNode(cx, ts, PN_UNARY, tc);
4022 if (!pn)
4023 return NULL;
4024 pn2 = BracketedExpr(cx, ts, tc);
4025 if (!pn2)
4026 return NULL;
4028 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
4029 pn->pn_type = TOK_RP;
4030 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
4031 pn->pn_kid = pn2;
4032 break;
4034 #if JS_HAS_XML_SUPPORT
4035 case TOK_STAR:
4036 pn = QualifiedIdentifier(cx, ts, tc);
4037 if (!pn)
4038 return NULL;
4039 notsharp = JS_TRUE;
4040 break;
4042 case TOK_AT:
4043 pn = AttributeIdentifier(cx, ts, tc);
4044 if (!pn)
4045 return NULL;
4046 notsharp = JS_TRUE;
4047 break;
4049 case TOK_XMLSTAGO:
4050 pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE);
4051 if (!pn)
4052 return NULL;
4053 notsharp = JS_TRUE; /* XXXbe could be sharp? */
4054 break;
4055 #endif /* JS_HAS_XML_SUPPORT */
4057 case TOK_STRING:
4058 #if JS_HAS_SHARP_VARS
4059 notsharp = JS_TRUE;
4060 /* FALL THROUGH */
4061 #endif
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 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4071 if (!pn)
4072 return NULL;
4073 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
4074 #if JS_HAS_XML_SUPPORT
4075 if (tt == TOK_XMLPI)
4076 pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2;
4077 else
4078 #endif
4079 pn->pn_op = CURRENT_TOKEN(ts).t_op;
4080 if (tt == TOK_NAME) {
4081 pn->pn_arity = PN_NAME;
4082 pn->pn_expr = NULL;
4083 pn->pn_slot = -1;
4084 pn->pn_attrs = 0;
4086 #if JS_HAS_XML_SUPPORT
4087 if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
4088 pn = QualifiedSuffix(cx, ts, pn, tc);
4089 if (!pn)
4090 return NULL;
4091 break;
4092 }
4093 #endif
4095 /* Unqualified __parent__ and __proto__ uses require activations. */
4096 if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
4097 pn->pn_atom == cx->runtime->atomState.protoAtom) {
4098 tc->flags |= TCF_FUN_HEAVYWEIGHT;
4099 } else {
4100 JSAtomListElement *ale;
4101 JSStackFrame *fp;
4102 JSStmtInfo *stmt;
4104 /* Measure optimizable global variable uses. */
4105 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
4106 if (ale &&
4107 !(fp = cx->fp)->fun &&
4108 fp->scopeChain == fp->varobj &&
4109 !js_InWithStatement(tc) &&
4110 !js_InCatchBlock(tc, pn->pn_atom)) {
4111 tc->globalUses++;
4112 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
4113 if (STMT_IS_LOOP(stmt)) {
4114 tc->loopyGlobalUses++;
4115 break;
4116 }
4117 }
4118 }
4119 }
4120 }
4121 break;
4123 case TOK_NUMBER:
4124 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4125 if (!pn)
4126 return NULL;
4127 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
4128 #if JS_HAS_SHARP_VARS
4129 notsharp = JS_TRUE;
4130 #endif
4131 break;
4133 case TOK_PRIMARY:
4134 pn = NewParseNode(cx, ts, PN_NULLARY, tc);
4135 if (!pn)
4136 return NULL;
4137 pn->pn_op = CURRENT_TOKEN(ts).t_op;
4138 #if JS_HAS_SHARP_VARS
4139 notsharp = JS_TRUE;
4140 #endif
4141 break;
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 return NULL;
4151 default:
4152 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4153 JSMSG_SYNTAX_ERROR);
4154 return NULL;
4155 }
4157 #if JS_HAS_SHARP_VARS
4158 if (defsharp) {
4159 if (notsharp) {
4160 badsharp:
4161 js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
4162 JSMSG_BAD_SHARP_VAR_DEF);
4163 return NULL;
4164 }
4165 defsharp->pn_kid = pn;
4166 return defsharp;
4167 }
4168 #endif
4169 return pn;
4170 }
4172 static JSBool
4173 ContainsVarStmt(JSParseNode *pn)
4174 {
4175 JSParseNode *pn2;
4177 if (!pn)
4178 return JS_FALSE;
4179 switch (pn->pn_arity) {
4180 case PN_LIST:
4181 if (pn->pn_type == TOK_VAR)
4182 return JS_TRUE;
4183 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4184 if (ContainsVarStmt(pn2))
4185 return JS_TRUE;
4186 }
4187 break;
4188 case PN_TERNARY:
4189 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 if (pn->pn_op != JSOP_NOP)
4198 return JS_FALSE;
4199 return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
4200 case PN_UNARY:
4201 if (pn->pn_op != JSOP_NOP)
4202 return JS_FALSE;
4203 return ContainsVarStmt(pn->pn_kid);
4204 default:;
4205 }
4206 return JS_FALSE;
4207 }
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 {
4216 if (pn->pn_type != type) {
4217 switch (type) {
4218 case TOK_NUMBER:
4219 if (pn->pn_type == TOK_STRING) {
4220 jsdouble d;
4221 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
4222 return JS_FALSE;
4223 pn->pn_dval = d;
4224 pn->pn_type = TOK_NUMBER;
4225 pn->pn_op = JSOP_NUMBER;
4226 }
4227 break;
4229 case TOK_STRING:
4230 if (pn->pn_type == TOK_NUMBER) {
4231 JSString *str = js_NumberToString(cx, pn->pn_dval);
4232 if (!str)
4233 return JS_FALSE;
4234 pn->pn_atom = js_AtomizeString(cx, str, 0);
4235 if (!pn->pn_atom)
4236 return JS_FALSE;
4237 pn->pn_type = TOK_STRING;
4238 pn->pn_op = JSOP_STRING;
4239 }
4240 break;
4242 default:;
4243 }
4244 }
4245 return JS_TRUE;
4246 }
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 {
4257 jsdouble d, d2;
4258 int32 i, j;
4259 uint32 u;
4261 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
4262 d = pn1->pn_dval;
4263 d2 = pn2->pn_dval;
4264 switch (op) {
4265 case JSOP_LSH:
4266 case JSOP_RSH:
4267 if (!js_DoubleToECMAInt32(cx, d, &i))
4268 return JS_FALSE;
4269 if (!js_DoubleToECMAInt32(cx, d2, &j))
4270 return JS_FALSE;
4271 j &= 31;
4272 d = (op == JSOP_LSH) ? i << j : i >> j;
4273 break;
4275 case JSOP_URSH:
4276 if (!js_DoubleToECMAUint32(cx, d, &u))
4277 return JS_FALSE;
4278 if (!js_DoubleToECMAInt32(cx, d2, &j))
4279 return JS_FALSE;
4280 j &= 31;
4281 d = u >> j;
4282 break;
4284 case JSOP_ADD:
4285 d += d2;
4286 break;
4288 case JSOP_SUB:
4289 d -= d2;
4290 break;
4292 case JSOP_MUL:
4293 d *= d2;
4294 break;
4296 case JSOP_DIV:
4297 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 if (d == 0 || JSDOUBLE_IS_NaN(d))
4305 d = *cx->runtime->jsNaN;
4306 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
4307 d = *cx->runtime->jsNegativeInfinity;
4308 else
4309 d = *cx->runtime->jsPositiveInfinity;
4310 } else {
4311 d /= d2;
4312 }
4313 break;
4315 case JSOP_MOD:
4316 if (d2 == 0) {
4317 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 d = fmod(d, d2);
4324 }
4325 break;
4327 default:;
4328 }
4330 /* Take care to allow pn1 or pn2 to alias pn. */
4331 if (pn1 != pn)
4332 RecycleTree(pn1, tc);
4333 if (pn2 != pn)
4334 RecycleTree(pn2, tc);
4335 pn->pn_type = TOK_NUMBER;
4336 pn->pn_op = JSOP_NUMBER;
4337 pn->pn_arity = PN_NULLARY;
4338 pn->pn_dval = d;
4339 return JS_TRUE;
4340 }
4342 #if JS_HAS_XML_SUPPORT
4344 static JSBool
4345 FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4346 {
4347 JSTokenType tt;
4348 JSParseNode **pnp, *pn1, *pn2;
4349 JSString *accum, *str;
4350 uint32 i, j;
4352 JS_ASSERT(pn->pn_arity == PN_LIST);
4353 tt = pn->pn_type;
4354 pnp = &pn->pn_head;
4355 pn1 = *pnp;
4356 accum = NULL;
4357 if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
4358 if (tt == TOK_XMLETAGO)
4359 accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom);
4360 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC)
4361 accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom);
4362 }
4364 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 switch (pn2->pn_type) {
4368 case TOK_XMLATTR:
4369 if (!accum)
4370 goto cantfold;
4371 /* FALL THROUGH */
4372 case TOK_XMLNAME:
4373 case TOK_XMLSPACE:
4374 case TOK_XMLTEXT:
4375 case TOK_STRING:
4376 if (pn2->pn_arity == PN_LIST)
4377 goto cantfold;
4378 str = ATOM_TO_STRING(pn2->pn_atom);
4379 break;
4381 case TOK_XMLCDATA:
4382 str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom));
4383 if (!str)
4384 return JS_FALSE;
4385 break;
4387 case TOK_XMLCOMMENT:
4388 str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom));
4389 if (!str)
4390 return JS_FALSE;
4391 break;
4393 case TOK_XMLPI:
4394 str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom),
4395 ATOM_TO_STRING(pn2->pn_atom2));
4396 if (!str)
4397 return JS_FALSE;
4398 break;
4400 cantfold:
4401 default:
4402 JS_ASSERT(*pnp == pn1);
4403 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 } else if (accum && pn1 != pn2) {
4410 while (pn1->pn_next != pn2) {
4411 pn1 = RecycleTree(pn1, tc);
4412 --pn->pn_count;
4413 }
4414 pn1->pn_type = TOK_XMLTEXT;
4415 pn1->pn_op = JSOP_STRING;
4416 pn1->pn_arity = PN_NULLARY;
4417 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
4418 if (!pn1->pn_atom)
4419 return JS_FALSE;
4420 JS_ASSERT(pnp != &pn1->pn_next);
4421 *pnp = pn1;
4422 }
4423 pnp = &pn2->pn_next;
4424 pn1 = *pnp;
4425 accum = NULL;
4426 continue;
4427 }
4429 if (accum) {
4430 str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0)
4431 ? js_AddAttributePart(cx, i & 1, accum, str)
4432 : js_ConcatStrings(cx, accum, str);
4433 if (!str)
4434 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 ++j;
4440 }
4441 accum = str;
4442 }
4444 if (accum) {
4445 str = NULL;
4446 if ((pn->pn_extra & PNX_CANTFOLD) == 0) {
4447 if (tt == TOK_XMLPTAGC)
4448 str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom);
4449 else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO)
4450 str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom);
4451 }
4452 if (str) {
4453 accum = js_ConcatStrings(cx, accum, str);
4454 if (!accum)
4455 return JS_FALSE;
4456 }
4458 JS_ASSERT(*pnp == pn1);
4459 while (pn1->pn_next) {
4460 pn1 = RecycleTree(pn1, tc);
4461 --pn->pn_count;
4462 }
4463 pn1->pn_type = TOK_XMLTEXT;
4464 pn1->pn_op = JSOP_STRING;
4465 pn1->pn_arity = PN_NULLARY;
4466 pn1->pn_atom = js_AtomizeString(cx, accum, 0);
4467 if (!pn1->pn_atom)
4468 return JS_FALSE;
4469 JS_ASSERT(pnp != &pn1->pn_next);
4470 *pnp = pn1;
4471 }
4473 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 if (!(pn->pn_extra & PNX_XMLROOT)) {
4482 PN_MOVE_NODE(pn, pn1);
4483 } else if (tt == TOK_XMLPTAGC) {
4484 pn->pn_type = TOK_XMLELEM;
4485 pn->pn_op = JSOP_TOXML;
4486 }
4487 }
4488 return JS_TRUE;
4489 }
4491 #endif /* JS_HAS_XML_SUPPORT */
4493 JSBool
4494 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4495 {
4496 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
4497 int stackDummy;
4499 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4500 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4501 return JS_FALSE;
4502 }
4504 switch (pn->pn_arity) {
4505 case PN_FUNC:
4506 {
4507 uint16 oldflags = tc->flags;
4509 tc->flags = (uint16) pn->pn_flags;
4510 if (!js_FoldConstants(cx, pn->pn_body, tc))
4511 return JS_FALSE;
4512 tc->flags = oldflags;
4513 break;
4514 }
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;
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 }
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;
4557 default:;
4558 }
4559 #endif
4561 /* Save the list head in pn1 for later use. */
4562 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4563 if (!js_FoldConstants(cx, pn2, tc))
4564 return JS_FALSE;
4565 }
4566 break;
4568 case PN_TERNARY:
4569 /* Any kid may be null (e.g. for (;;)). */
4570 pn1 = pn->pn_kid1;
4571 pn2 = pn->pn_kid2;
4572 pn3 = pn->pn_kid3;
4573 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4574 return JS_FALSE;
4575 if (pn2 && !js_FoldConstants(cx, pn2, tc))
4576 return JS_FALSE;
4577 if (pn3 && !js_FoldConstants(cx, pn3, tc))
4578 return JS_FALSE;
4579 break;
4581 case PN_BINARY:
4582 /* First kid may be null (for default case in switch). */
4583 pn1 = pn->pn_left;
4584 pn2 = pn->pn_right;
4585 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4586 return JS_FALSE;
4587 if (!js_FoldConstants(cx, pn2, tc))
4588 return JS_FALSE;
4589 break;
4591 case PN_UNARY:
4592 /* Our kid may be null (e.g. return; vs. return e;). */
4593 pn1 = pn->pn_kid;
4594 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4595 return JS_FALSE;
4596 break;
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 pn1 = pn->pn_expr;
4606 while (pn1 && pn1->pn_arity == PN_NAME)
4607 pn1 = pn1->pn_expr;
4608 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4609 return JS_FALSE;
4610 break;
4612 case PN_NULLARY:
4613 break;
4614 }
4616 switch (pn->pn_type) {
4617 case TOK_IF:
4618 if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
4619 break;
4620 /* FALL THROUGH */
4622 case TOK_HOOK:
4623 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
4624 switch (pn1->pn_type) {
4625 case TOK_NUMBER:
4626 if (pn1->pn_dval == 0)
4627 pn2 = pn3;
4628 break;
4629 case TOK_STRING:
4630 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
4631 pn2 = pn3;
4632 break;
4633 case TOK_PRIMARY:
4634 if (pn1->pn_op == JSOP_TRUE)
4635 break;
4636 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
4637 pn2 = pn3;
4638 break;
4639 }
4640 /* FALL THROUGH */
4641 default:
4642 /* Early return to dodge common code that copies pn2 to pn. */
4643 return JS_TRUE;
4644 }
4646 if (pn2) {
4647 /* pn2 is the then- or else-statement subtree to compile. */
4648 PN_MOVE_NODE(pn, pn2);
4649 } else {
4650 /* False condition and no else: make pn an empty statement. */
4651 pn->pn_type = TOK_SEMI;
4652 pn->pn_arity = PN_UNARY;
4653 pn->pn_kid = NULL;
4654 }
4655 RecycleTree(pn2, tc);
4656 if (pn3 && pn3 != pn2)
4657 RecycleTree(pn3, tc);
4658 break;
4660 case TOK_PLUS:
4661 if (pn->pn_arity == PN_LIST) {
4662 size_t length, length2;
4663 jschar *chars;
4664 JSString *str, *str2;
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 if (pn->pn_extra & PNX_CANTFOLD)
4673 return JS_TRUE;
4674 if (pn->pn_extra != PNX_STRCAT)
4675 goto do_binary_op;
4677 /* Ok, we're concatenating: convert non-string constant operands. */
4678 length = 0;
4679 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4680 if (!FoldType(cx, pn2, TOK_STRING))
4681 return JS_FALSE;
4682 /* XXX fold only if all operands convert to string */
4683 if (pn2->pn_type != TOK_STRING)
4684 return JS_TRUE;
4685 length += ATOM_TO_STRING(pn2->pn_atom)->length;
4686 }
4688 /* Allocate a new buffer and string descriptor for the result. */
4689 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
4690 if (!chars)
4691 return JS_FALSE;
4692 str = js_NewString(cx, chars, length, 0);
4693 if (!str) {
4694 JS_free(cx, chars);
4695 return JS_FALSE;
4696 }
4698 /* Fill the buffer, advancing chars and recycling kids as we go. */
4699 for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) {
4700 str2 = ATOM_TO_STRING(pn2->pn_atom);
4701 length2 = str2->length;
4702 js_strncpy(chars, str2->chars, length2);
4703 chars += length2;
4704 }
4705 *chars = 0;
4707 /* Atomize the result string and mutate pn to refer to it. */
4708 pn->pn_atom = js_AtomizeString(cx, str, 0);
4709 if (!pn->pn_atom)
4710 return JS_FALSE;
4711 pn->pn_type = TOK_STRING;
4712 pn->pn_op = JSOP_STRING;
4713 pn->pn_arity = PN_NULLARY;
4714 break;
4715 }
4717 /* Handle a binary string concatenation. */
4718 JS_ASSERT(pn->pn_arity == PN_BINARY);
4719 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
4720 JSString *left, *right, *str;
4722 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
4723 TOK_STRING)) {
4724 return JS_FALSE;
4725 }
4726 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
4727 return JS_TRUE;
4728 left = ATOM_TO_STRING(pn1->pn_atom);
4729 right = ATOM_TO_STRING(pn2->pn_atom);
4730 str = js_ConcatStrings(cx, left, right);
4731 if (!str)
4732 return JS_FALSE;
4733 pn->pn_atom = js_AtomizeString(cx, str, 0);
4734 if (!pn->pn_atom)
4735 return JS_FALSE;
4736 pn->pn_type = TOK_STRING;
4737 pn->pn_op = JSOP_STRING;
4738 pn->pn_arity = PN_NULLARY;
4739 RecycleTree(pn1, tc);
4740 RecycleTree(pn2, tc);
4741 break;
4742 }
4744 /* Can't concatenate string literals, let's try numbers. */
4745 goto do_binary_op;
4747 case TOK_STAR:
4748 /* The * in 'import *;' parses as a nullary star node. */
4749 if (pn->pn_arity == PN_NULLARY)
4750 break;
4751 /* FALL THROUGH */
4753 case TOK_SHOP:
4754 case TOK_MINUS:
4755 case TOK_DIVOP:
4756 do_binary_op:
4757 if (pn->pn_arity == PN_LIST) {
4758 JS_ASSERT(pn->pn_count > 2);
4759 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4760 if (!FoldType(cx, pn2, TOK_NUMBER))
4761 return JS_FALSE;
4762 /* XXX fold only if all operands convert to number */
4763 if (pn2->pn_type != TOK_NUMBER)
4764 break;
4765 }
4766 if (!pn2) {
4767 JSOp op = pn->pn_op;
4769 pn2 = pn1->pn_next;
4770 pn3 = pn2->pn_next;
4771 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
4772 return JS_FALSE;
4773 while ((pn2 = pn3) != NULL) {
4774 pn3 = pn2->pn_next;
4775 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
4776 return JS_FALSE;
4777 }
4778 }
4779 } else {
4780 JS_ASSERT(pn->pn_arity == PN_BINARY);
4781 if (!FoldType(cx, pn1, TOK_NUMBER) ||
4782 !FoldType(cx, pn2, TOK_NUMBER)) {
4783 return JS_FALSE;
4784 }
4785 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
4786 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
4787 return JS_FALSE;
4788 }
4789 }
4790 break;
4792 case TOK_UNARYOP:
4793 if (pn1->pn_type == TOK_NUMBER) {
4794 jsdouble d;
4795 int32 i;
4797 /* Operate on one numeric constant. */
4798 d = pn1->pn_dval;
4799 switch (pn->pn_op) {
4800 case JSOP_BITNOT:
4801 if (!js_DoubleToECMAInt32(cx, d, &i))
4802 return JS_FALSE;
4803 d = ~i;
4804 break;
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 d = -d;
4816 #endif
4817 break;
4819 case JSOP_POS:
4820 break;
4822 case JSOP_NOT:
4823 pn->pn_type = TOK_PRIMARY;
4824 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
4825 pn->pn_arity = PN_NULLARY;
4826 /* FALL THROUGH */
4828 default:
4829 /* Return early to dodge the common TOK_NUMBER code. */
4830 return JS_TRUE;
4831 }
4832 pn->pn_type = TOK_NUMBER;
4833 pn->pn_op = JSOP_NUMBER;
4834 pn->pn_arity = PN_NULLARY;
4835 pn->pn_dval = d;
4836 RecycleTree(pn1, tc);
4837 }
4838 break;
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 if (pn->pn_arity == PN_LIST) {
4848 JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
4849 if (!FoldXMLConstants(cx, pn, tc))
4850 return JS_FALSE;
4851 }
4852 break;
4854 case TOK_AT:
4855 if (pn1->pn_type == TOK_XMLNAME) {
4856 jsval v;
4857 JSAtom *atom;
4859 v = ATOM_KEY(pn1->pn_atom);
4860 if (!js_ToAttributeName(cx, &v))
4861 return JS_FALSE;
4862 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
4863 atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0);
4864 if (!atom)
4865 return JS_FALSE;
4867 pn->pn_type = TOK_XMLNAME;
4868 pn->pn_op = JSOP_OBJECT;
4869 pn->pn_arity = PN_NULLARY;
4870 pn->pn_atom = atom;
4871 RecycleTree(pn1, tc);
4872 }
4873 break;
4874 #endif /* JS_HAS_XML_SUPPORT */
4876 default:;
4877 }
4879 return JS_TRUE;
4880 }