implement breakpoint API (breakpoints don't actually trigger yet)
[swfdec.git] / libswfdec / js / jsparse.c
blobdbb892e912a010c8d4ebdd7031b769af013bfecf
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
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.
24 * Contributor(s):
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.
38 * ***** END LICENSE BLOCK ***** */
41 * JS parser.
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.
51 * This parser attempts no error recovery. The dense JSTokenType enumeration
52 * was designed with error recovery built on 64-bit first and follow bitsets
53 * in mind, however.
55 #include "jsstddef.h"
56 #include <stdlib.h>
57 #include <string.h>
58 #include <math.h>
59 #include "jstypes.h"
60 #include "jsarena.h" /* Added by JSIFY */
61 #include "jsutil.h" /* Added by JSIFY */
62 #include "jsapi.h"
63 #include "jsatom.h"
64 #include "jscntxt.h"
65 #include "jsconfig.h"
66 #include "jsemit.h"
67 #include "jsfun.h"
68 #include "jsinterp.h"
69 #include "jslock.h"
70 #include "jsnum.h"
71 #include "jsobj.h"
72 #include "jsopcode.h"
73 #include "jsparse.h"
74 #include "jsscan.h"
75 #include "jsscope.h"
76 #include "jsscript.h"
77 #include "jsstr.h"
80 * JS parsers, from lowest to highest precedence.
82 * Each parser takes a context and a token stream, and emits bytecode using
83 * a code generator.
86 typedef JSParseNode *
87 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
89 typedef JSParseNode *
90 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
91 JSBool allowCallSyntax);
93 static JSParser FunctionStmt;
94 #if JS_HAS_LEXICAL_CLOSURE
95 static JSParser FunctionExpr;
96 #endif
97 static JSParser Statements;
98 static JSParser Statement;
99 static JSParser Variables;
100 static JSParser Expr;
101 static JSParser AssignExpr;
102 static JSParser CondExpr;
103 static JSParser OrExpr;
104 static JSParser AndExpr;
105 static JSParser BitOrExpr;
106 static JSParser BitXorExpr;
107 static JSParser BitAndExpr;
108 static JSParser EqExpr;
109 static JSParser RelExpr;
110 static JSParser ShiftExpr;
111 static JSParser AddExpr;
112 static JSParser MulExpr;
113 static JSParser UnaryExpr;
114 static JSMemberParser MemberExpr;
115 static JSParser PrimaryExpr;
118 * Insist that the next token be of type tt, or report errno and return null.
119 * NB: this macro uses cx and ts from its lexical environment.
121 #define MUST_MATCH_TOKEN(tt, errno) \
122 JS_BEGIN_MACRO \
123 if (js_GetToken(cx, ts) != tt) { \
124 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
125 return NULL; \
127 JS_END_MACRO
129 #define CHECK_RECURSION() \
130 JS_BEGIN_MACRO \
131 int stackDummy; \
132 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \
133 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
134 JSMSG_OVER_RECURSED); \
135 return NULL; \
137 JS_END_MACRO
139 #ifdef METER_PARSENODES
140 static uint32 parsenodes = 0;
141 static uint32 maxparsenodes = 0;
142 static uint32 recyclednodes = 0;
143 #endif
145 static void
146 RecycleTree(JSParseNode *pn, JSTreeContext *tc)
148 if (!pn)
149 return;
150 JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */
151 pn->pn_next = tc->nodeList;
152 tc->nodeList = pn;
153 #ifdef METER_PARSENODES
154 recyclednodes++;
155 #endif
158 static JSParseNode *
159 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
161 JSParseNode *pn;
163 pn = tc->nodeList;
164 if (!pn) {
165 JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
166 if (!pn)
167 JS_ReportOutOfMemory(cx);
168 } else {
169 tc->nodeList = pn->pn_next;
171 /* Recycle immediate descendents only, to save work and working set. */
172 switch (pn->pn_arity) {
173 case PN_FUNC:
174 RecycleTree(pn->pn_body, tc);
175 break;
176 case PN_LIST:
177 if (pn->pn_head) {
178 /* XXX check for dup recycles in the list */
179 *pn->pn_tail = tc->nodeList;
180 tc->nodeList = pn->pn_head;
181 #ifdef METER_PARSENODES
182 recyclednodes += pn->pn_count;
183 #endif
185 break;
186 case PN_TERNARY:
187 RecycleTree(pn->pn_kid1, tc);
188 RecycleTree(pn->pn_kid2, tc);
189 RecycleTree(pn->pn_kid3, tc);
190 break;
191 case PN_BINARY:
192 RecycleTree(pn->pn_left, tc);
193 RecycleTree(pn->pn_right, tc);
194 break;
195 case PN_UNARY:
196 RecycleTree(pn->pn_kid, tc);
197 break;
198 case PN_NAME:
199 RecycleTree(pn->pn_expr, tc);
200 break;
201 case PN_NULLARY:
202 break;
205 return pn;
209 * Allocate a JSParseNode from cx's temporary arena.
211 static JSParseNode *
212 NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity,
213 JSTreeContext *tc)
215 JSParseNode *pn;
217 pn = NewOrRecycledNode(cx, tc);
218 if (!pn)
219 return NULL;
220 pn->pn_type = tok->type;
221 pn->pn_pos = tok->pos;
222 pn->pn_op = JSOP_NOP;
223 pn->pn_arity = arity;
224 pn->pn_next = NULL;
225 #ifdef METER_PARSENODES
226 parsenodes++;
227 if (parsenodes - recyclednodes > maxparsenodes)
228 maxparsenodes = parsenodes - recyclednodes;
229 #endif
230 return pn;
233 static JSParseNode *
234 NewBinary(JSContext *cx, JSTokenType tt,
235 JSOp op, JSParseNode *left, JSParseNode *right,
236 JSTreeContext *tc)
238 JSParseNode *pn, *pn1, *pn2;
240 if (!left || !right)
241 return NULL;
244 * Flatten a left-associative (left-heavy) tree of a given operator into
245 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
247 if (left->pn_type == tt &&
248 left->pn_op == op &&
249 (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
250 if (left->pn_arity != PN_LIST) {
251 pn1 = left->pn_left, pn2 = left->pn_right;
252 left->pn_arity = PN_LIST;
253 PN_INIT_LIST_1(left, pn1);
254 PN_APPEND(left, pn2);
255 left->pn_extra = 0;
256 if (tt == TOK_PLUS) {
257 if (pn1->pn_type == TOK_STRING)
258 left->pn_extra |= PNX_STRCAT;
259 else if (pn1->pn_type != TOK_NUMBER)
260 left->pn_extra |= PNX_CANTFOLD;
261 if (pn2->pn_type == TOK_STRING)
262 left->pn_extra |= PNX_STRCAT;
263 else if (pn2->pn_type != TOK_NUMBER)
264 left->pn_extra |= PNX_CANTFOLD;
267 PN_APPEND(left, right);
268 left->pn_pos.end = right->pn_pos.end;
269 if (tt == TOK_PLUS) {
270 if (right->pn_type == TOK_STRING)
271 left->pn_extra |= PNX_STRCAT;
272 else if (right->pn_type != TOK_NUMBER)
273 left->pn_extra |= PNX_CANTFOLD;
275 return left;
279 * Fold constant addition immediately, to conserve node space and, what's
280 * more, so js_FoldConstants never sees mixed addition and concatenation
281 * operations with more than one leading non-string operand in a PN_LIST
282 * generated for expressions such as 1 + 2 + "pt" (which should evaluate
283 * to "3pt", not "12pt").
285 if (tt == TOK_PLUS &&
286 left->pn_type == TOK_NUMBER &&
287 right->pn_type == TOK_NUMBER) {
288 left->pn_dval += right->pn_dval;
289 RecycleTree(right, tc);
290 return left;
293 pn = NewOrRecycledNode(cx, tc);
294 if (!pn)
295 return NULL;
296 pn->pn_type = tt;
297 pn->pn_pos.begin = left->pn_pos.begin;
298 pn->pn_pos.end = right->pn_pos.end;
299 pn->pn_op = op;
300 pn->pn_arity = PN_BINARY;
301 pn->pn_left = left;
302 pn->pn_right = right;
303 pn->pn_next = NULL;
304 #ifdef METER_PARSENODES
305 parsenodes++;
306 if (parsenodes - recyclednodes > maxparsenodes)
307 maxparsenodes = parsenodes - recyclednodes;
308 #endif
309 return pn;
312 #if JS_HAS_GETTER_SETTER
313 static JSTokenType
314 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
316 JSAtom *atom;
317 JSRuntime *rt;
318 JSOp op;
319 const char *name;
321 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
322 atom = CURRENT_TOKEN(ts).t_atom;
323 rt = cx->runtime;
324 if (atom == rt->atomState.getterAtom)
325 op = JSOP_GETTER;
326 else if (atom == rt->atomState.setterAtom)
327 op = JSOP_SETTER;
328 else
329 return TOK_NAME;
330 if (js_PeekTokenSameLine(cx, ts) != tt)
331 return TOK_NAME;
332 (void) js_GetToken(cx, ts);
333 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
334 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
335 JSMSG_BAD_GETTER_OR_SETTER,
336 (op == JSOP_GETTER)
337 ? js_getter_str
338 : js_setter_str);
339 return TOK_ERROR;
341 CURRENT_TOKEN(ts).t_op = op;
342 name = js_AtomToPrintableString(cx, atom);
343 if (!name ||
344 !js_ReportCompileErrorNumber(cx, ts, NULL,
345 JSREPORT_WARNING |
346 JSREPORT_STRICT,
347 JSMSG_DEPRECATED_USAGE,
348 name)) {
349 return TOK_ERROR;
351 return tt;
353 #endif
356 * Parse a top-level JS script.
358 JS_FRIEND_API(JSParseNode *)
359 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
361 JSStackFrame *fp, frame;
362 JSTreeContext tc;
363 JSParseNode *pn;
366 * Push a compiler frame if we have no frames, or if the top frame is a
367 * lightweight function activation, or if its scope chain doesn't match
368 * the one passed to us.
370 fp = cx->fp;
371 if (!fp || !fp->varobj || fp->scopeChain != chain) {
372 memset(&frame, 0, sizeof frame);
373 frame.varobj = frame.scopeChain = chain;
374 if (cx->options & JSOPTION_VAROBJFIX) {
375 while ((chain = JS_GetParent(cx, chain)) != NULL)
376 frame.varobj = chain;
378 frame.down = fp;
379 cx->fp = &frame;
383 * Protect atoms from being collected by a GC activation, which might
384 * - nest on this thread due to out of memory (the so-called "last ditch"
385 * GC attempted within js_AllocGCThing), or
386 * - run for any reason on another thread if this thread is suspended on
387 * an object lock before it finishes generating bytecode into a script
388 * protected from the GC by a root or a stack frame reference.
390 JS_KEEP_ATOMS(cx->runtime);
391 TREE_CONTEXT_INIT(&tc);
392 pn = Statements(cx, ts, &tc);
393 if (pn) {
394 if (!js_MatchToken(cx, ts, TOK_EOF)) {
395 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
396 JSMSG_SYNTAX_ERROR);
397 pn = NULL;
398 } else {
399 pn->pn_type = TOK_LC;
400 if (!js_FoldConstants(cx, pn, &tc))
401 pn = NULL;
405 TREE_CONTEXT_FINISH(&tc);
406 JS_UNKEEP_ATOMS(cx->runtime);
407 cx->fp = fp;
408 return pn;
412 * Compile a top-level script.
414 JS_FRIEND_API(JSBool)
415 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
416 JSCodeGenerator *cg)
418 JSStackFrame *fp, frame;
419 uint32 flags;
420 JSParseNode *pn;
421 JSBool ok;
422 #ifdef METER_PARSENODES
423 void *sbrk(ptrdiff_t), *before = sbrk(0);
424 #endif
427 * Push a compiler frame if we have no frames, or if the top frame is a
428 * lightweight function activation, or if its scope chain doesn't match
429 * the one passed to us.
431 fp = cx->fp;
432 if (!fp || !fp->varobj || fp->scopeChain != chain) {
433 memset(&frame, 0, sizeof frame);
434 frame.varobj = frame.scopeChain = chain;
435 if (cx->options & JSOPTION_VAROBJFIX) {
436 while ((chain = JS_GetParent(cx, chain)) != NULL)
437 frame.varobj = chain;
439 frame.down = fp;
440 cx->fp = &frame;
442 flags = cx->fp->flags;
443 cx->fp->flags = flags |
444 (JS_HAS_COMPILE_N_GO_OPTION(cx)
445 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
446 : JSFRAME_COMPILING);
448 /* Prevent GC activation while compiling. */
449 JS_KEEP_ATOMS(cx->runtime);
451 pn = Statements(cx, ts, &cg->treeContext);
452 if (!pn) {
453 ok = JS_FALSE;
454 } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
455 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
456 JSMSG_SYNTAX_ERROR);
457 ok = JS_FALSE;
458 } else {
459 #ifdef METER_PARSENODES
460 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
461 (char *)sbrk(0) - (char *)before,
462 parsenodes,
463 maxparsenodes,
464 parsenodes - recyclednodes);
465 before = sbrk(0);
466 #endif
469 * No need to emit code here -- Statements already has, for each
470 * statement in turn. Search for TCF_COMPILING in Statements, below.
471 * That flag is set for every tc == &cg->treeContext, and it implies
472 * that the tc can be downcast to a cg and used to emit code during
473 * parsing, rather than at the end of the parse phase.
475 JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
476 ok = JS_TRUE;
479 #ifdef METER_PARSENODES
480 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
481 (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
482 #endif
483 #ifdef JS_ARENAMETER
484 JS_DumpArenaStats(stdout);
485 #endif
486 JS_UNKEEP_ATOMS(cx->runtime);
487 cx->fp->flags = flags;
488 cx->fp = fp;
489 return ok;
493 * Insist on a final return before control flows out of pn, but don't be too
494 * smart about loops (do {...; return e2;} while(0) at the end of a function
495 * that contains an early return e1 will get a strict-option-only warning).
497 #define ENDS_IN_OTHER 0
498 #define ENDS_IN_RETURN 1
499 #define ENDS_IN_BREAK 2
501 static int
502 HasFinalReturn(JSParseNode *pn)
504 uintN rv, rv2, hasDefault;
505 JSParseNode *pn2, *pn3;
507 switch (pn->pn_type) {
508 case TOK_LC:
509 if (!pn->pn_head)
510 return ENDS_IN_OTHER;
511 return HasFinalReturn(PN_LAST(pn));
513 case TOK_IF:
514 rv = HasFinalReturn(pn->pn_kid2);
515 if (pn->pn_kid3)
516 rv &= HasFinalReturn(pn->pn_kid3);
517 return rv;
519 #if JS_HAS_SWITCH_STATEMENT
520 case TOK_SWITCH:
521 rv = ENDS_IN_RETURN;
522 hasDefault = ENDS_IN_OTHER;
523 for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
524 if (pn2->pn_type == TOK_DEFAULT)
525 hasDefault = ENDS_IN_RETURN;
526 pn3 = pn2->pn_right;
527 JS_ASSERT(pn3->pn_type == TOK_LC);
528 if (pn3->pn_head) {
529 rv2 = HasFinalReturn(PN_LAST(pn3));
530 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
531 /* Falling through to next case or default. */;
532 else
533 rv &= rv2;
536 /* If a final switch has no default case, we judge it harshly. */
537 rv &= hasDefault;
538 return rv;
539 #endif /* JS_HAS_SWITCH_STATEMENT */
541 case TOK_BREAK:
542 return ENDS_IN_BREAK;
544 case TOK_WITH:
545 return HasFinalReturn(pn->pn_right);
547 case TOK_RETURN:
548 return ENDS_IN_RETURN;
550 case TOK_COLON:
551 return HasFinalReturn(pn->pn_expr);
553 #if JS_HAS_EXCEPTIONS
554 case TOK_THROW:
555 return ENDS_IN_RETURN;
557 case TOK_TRY:
558 /* If we have a finally block that returns, we are done. */
559 if (pn->pn_kid3) {
560 rv = HasFinalReturn(pn->pn_kid3);
561 if (rv == ENDS_IN_RETURN)
562 return rv;
565 /* Else check the try block and any and all catch statements. */
566 rv = HasFinalReturn(pn->pn_kid1);
567 if (pn->pn_kid2)
568 rv &= HasFinalReturn(pn->pn_kid2);
569 return rv;
571 case TOK_CATCH:
572 /* Check this block's code and iterate over further catch blocks. */
573 rv = HasFinalReturn(pn->pn_kid3);
574 for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
575 rv &= HasFinalReturn(pn2->pn_kid3);
576 return rv;
577 #endif
579 default:
580 return ENDS_IN_OTHER;
584 static JSBool
585 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
587 JSFunction *fun;
588 JSBool ok;
590 fun = cx->fp->fun;
591 if (fun->atom) {
592 char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
593 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
594 JSREPORT_WARNING |
595 JSREPORT_STRICT,
596 JSMSG_NO_RETURN_VALUE, name);
597 } else {
598 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
599 JSREPORT_WARNING |
600 JSREPORT_STRICT,
601 JSMSG_ANON_NO_RETURN_VALUE);
603 return ok;
606 static JSBool
607 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
609 return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
612 static JSParseNode *
613 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
614 JSTreeContext *tc)
616 JSStackFrame *fp, frame;
617 JSObject *funobj;
618 uintN oldflags;
619 JSParseNode *pn;
621 fp = cx->fp;
622 funobj = fun->object;
623 if (!fp || fp->fun != fun || fp->varobj != funobj ||
624 fp->scopeChain != funobj) {
625 memset(&frame, 0, sizeof frame);
626 frame.fun = fun;
627 frame.varobj = frame.scopeChain = funobj;
628 frame.down = fp;
629 cx->fp = &frame;
632 oldflags = tc->flags;
633 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
634 tc->flags |= TCF_IN_FUNCTION;
635 pn = Statements(cx, ts, tc);
637 /* Check for falling off the end of a function that returns a value. */
638 if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
639 if (!CheckFinalReturn(cx, ts, pn))
640 pn = NULL;
643 cx->fp = fp;
644 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
645 return pn;
649 * Compile a JS function body, which might appear as the value of an event
650 * handler attribute in an HTML <INPUT> tag.
652 JSBool
653 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
655 JSArenaPool codePool, notePool;
656 JSCodeGenerator funcg;
657 JSStackFrame *fp, frame;
658 JSObject *funobj;
659 JSParseNode *pn;
660 JSBool ok;
662 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
663 JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
664 if (!js_InitCodeGenerator(cx, &funcg, &codePool, &notePool,
665 ts->filename, ts->lineno,
666 ts->principals)) {
667 return JS_FALSE;
670 /* Prevent GC activation while compiling. */
671 JS_KEEP_ATOMS(cx->runtime);
673 /* Push a JSStackFrame for use by FunctionBody. */
674 fp = cx->fp;
675 funobj = fun->object;
676 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
677 fp->scopeChain != funobj));
678 memset(&frame, 0, sizeof frame);
679 frame.fun = fun;
680 frame.varobj = frame.scopeChain = funobj;
681 frame.down = fp;
682 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
683 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
684 : JSFRAME_COMPILING;
685 cx->fp = &frame;
687 /* Ensure that the body looks like a block statement to js_EmitTree. */
688 CURRENT_TOKEN(ts).type = TOK_LC;
689 pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
690 if (!pn) {
691 ok = JS_FALSE;
692 } else {
694 * No need to emit code here -- Statements (via FunctionBody) already
695 * has. See similar comment in js_CompileTokenStream, and bug 108257.
697 fun->script = js_NewScriptFromCG(cx, &funcg, fun);
698 if (!fun->script) {
699 ok = JS_FALSE;
700 } else {
701 if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
702 fun->flags |= JSFUN_HEAVYWEIGHT;
703 ok = JS_TRUE;
707 /* Restore saved state and release code generation arenas. */
708 cx->fp = fp;
709 JS_UNKEEP_ATOMS(cx->runtime);
710 js_FinishCodeGenerator(cx, &funcg);
711 JS_FinishArenaPool(&codePool);
712 JS_FinishArenaPool(&notePool);
713 return ok;
716 static JSParseNode *
717 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
718 JSBool lambda)
720 JSParseNode *pn, *body;
721 JSOp op, prevop;
722 JSAtom *funAtom, *argAtom;
723 JSFunction *fun;
724 JSObject *parent;
725 JSObject *pobj;
726 JSProperty *prop;
727 uintN dupflag;
728 JSBool ok;
729 JSTreeContext funtc;
730 JSAtomListElement *ale;
732 /* Make a TOK_FUNCTION node. */
733 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc);
734 if (!pn)
735 return NULL;
736 #if JS_HAS_GETTER_SETTER
737 op = CURRENT_TOKEN(ts).t_op;
738 #endif
740 /* Scan the optional function name into funAtom. */
741 if (js_MatchToken(cx, ts, TOK_NAME))
742 funAtom = CURRENT_TOKEN(ts).t_atom;
743 else
744 funAtom = NULL;
746 /* Find the nearest variable-declaring scope and use it as our parent. */
747 parent = cx->fp->varobj;
748 fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, parent,
749 funAtom);
750 if (!fun)
751 return NULL;
753 #if JS_HAS_GETTER_SETTER
754 if (op != JSOP_NOP)
755 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
756 #endif
758 /* Now parse formal argument list and compute fun->nargs. */
759 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
760 if (!js_MatchToken(cx, ts, TOK_RP)) {
761 do {
762 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
763 argAtom = CURRENT_TOKEN(ts).t_atom;
764 pobj = NULL;
765 if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,
766 &prop)) {
767 return NULL;
769 dupflag = 0;
770 if (prop) {
771 ok = JS_TRUE;
772 if (pobj == fun->object &&
773 ((JSScopeProperty *) prop)->getter == js_GetArgument) {
774 const char *name = js_AtomToPrintableString(cx, argAtom);
777 * A duplicate parameter name. We force a duplicate node
778 * on the SCOPE_LAST_PROP(scope) list with the same id,
779 * distinguished by the SPROP_IS_DUPLICATE flag, and not
780 * mapped by an entry in scope.
782 ok = name &&
783 js_ReportCompileErrorNumber(cx, ts, NULL,
784 JSREPORT_WARNING |
785 JSREPORT_STRICT,
786 JSMSG_DUPLICATE_FORMAL,
787 name);
789 dupflag = SPROP_IS_DUPLICATE;
791 OBJ_DROP_PROPERTY(cx, pobj, prop);
792 if (!ok)
793 return NULL;
794 prop = NULL;
796 if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom,
797 js_GetArgument, js_SetArgument,
798 SPROP_INVALID_SLOT,
799 JSPROP_ENUMERATE | JSPROP_PERMANENT |
800 JSPROP_SHARED,
801 SPROP_HAS_SHORTID | dupflag,
802 fun->nargs)) {
803 return NULL;
805 fun->nargs++;
806 } while (js_MatchToken(cx, ts, TOK_COMMA));
808 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
811 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
812 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
814 TREE_CONTEXT_INIT(&funtc);
815 body = FunctionBody(cx, ts, fun, &funtc);
816 if (!body)
817 return NULL;
819 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
820 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
822 #if JS_HAS_LEXICAL_CLOSURE
824 * If we collected flags that indicate nested heavyweight functions, or
825 * this function contains heavyweight-making statements (references to
826 * __parent__ or __proto__; use of with, eval, import, or export; and
827 * assignment to arguments), flag the function as heavyweight (requiring
828 * a call object per invocation).
830 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
831 fun->flags |= JSFUN_HEAVYWEIGHT;
832 tc->flags |= TCF_FUN_HEAVYWEIGHT;
833 } else {
835 * If this function is a named statement function not at top-level
836 * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
837 * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
838 * enclosing function, if any, must be heavyweight.
840 if ((!lambda && funAtom && tc->topStmt) ||
841 (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
842 tc->flags |= TCF_FUN_HEAVYWEIGHT;
845 #endif
848 * Record names for function statements in tc->decls so we know when to
849 * avoid optimizing variable references that might name a function.
851 if (!lambda && funAtom) {
852 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
853 if (ale) {
854 prevop = ALE_JSOP(ale);
855 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
856 const char *name = js_AtomToPrintableString(cx, funAtom);
857 if (!name ||
858 !js_ReportCompileErrorNumber(cx, ts, NULL,
859 (prevop != JSOP_DEFCONST)
860 ? JSREPORT_WARNING |
861 JSREPORT_STRICT
862 : JSREPORT_ERROR,
863 JSMSG_REDECLARED_VAR,
864 (prevop == JSOP_DEFFUN ||
865 prevop == JSOP_CLOSURE)
866 ? js_function_str
867 : (prevop == JSOP_DEFCONST)
868 ? js_const_str
869 : js_var_str,
870 name)) {
871 return NULL;
874 if (tc->topStmt && prevop == JSOP_DEFVAR)
875 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
876 } else {
877 ale = js_IndexAtom(cx, funAtom, &tc->decls);
878 if (!ale)
879 return NULL;
881 ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
883 #if JS_HAS_LEXICAL_CLOSURE
885 * A function nested at top level inside another's body needs only a
886 * local variable to bind its name to its value, and not an activation
887 * object property (it might also need the activation property, if the
888 * outer function contains with statements, e.g., but the stack slot
889 * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
890 * JSOP_GETVAR bytecode).
892 if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
893 JSStackFrame *fp;
894 JSObject *varobj;
897 * Define a property on the outer function so that LookupArgOrVar
898 * can properly optimize accesses.
900 * XXX Here and in Variables, we use the function object's scope,
901 * XXX arguably polluting it, when we could use a compiler-private
902 * XXX scope structure. Tradition!
904 fp = cx->fp;
905 varobj = fp->varobj;
906 JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
907 JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
908 if (!js_LookupProperty(cx, varobj, (jsid)funAtom, &pobj, &prop))
909 return NULL;
910 if (prop)
911 OBJ_DROP_PROPERTY(cx, pobj, prop);
912 if (!prop || pobj != varobj) {
913 if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom,
914 OBJECT_TO_JSVAL(fun->object),
915 js_GetLocalVariable,
916 js_SetLocalVariable,
917 JSPROP_ENUMERATE,
918 SPROP_HAS_SHORTID, fp->fun->nvars,
919 NULL)) {
920 return NULL;
922 fp->fun->nvars++;
925 #endif
928 #if JS_HAS_LEXICAL_CLOSURE
929 if (lambda || !funAtom) {
931 * ECMA ed. 3 standard: function expression, possibly anonymous (even
932 * if at top-level, an unnamed function is an expression statement, not
933 * a function declaration).
935 op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
936 } else if (tc->topStmt) {
938 * ECMA ed. 3 extension: a function expression statement not at the
939 * top level, e.g., in a compound statement such as the "then" part
940 * of an "if" statement, binds a closure only if control reaches that
941 * sub-statement.
943 op = JSOP_CLOSURE;
944 } else
945 #endif
946 op = JSOP_NOP;
949 * Pending a better automatic GC root management scheme (see Mozilla bug
950 * 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to
951 * atomize here to protect against a GC activation.
953 pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
954 if (!pn->pn_funAtom)
955 return NULL;
957 pn->pn_op = op;
958 pn->pn_body = body;
959 pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;
960 pn->pn_tryCount = funtc.tryCount;
961 TREE_CONTEXT_FINISH(&funtc);
962 return pn;
965 static JSParseNode *
966 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
968 return FunctionDef(cx, ts, tc, JS_FALSE);
971 #if JS_HAS_LEXICAL_CLOSURE
972 static JSParseNode *
973 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
975 return FunctionDef(cx, ts, tc, JS_TRUE);
977 #endif
980 * Parse the statements in a block, creating a TOK_LC node that lists the
981 * statements' trees. If called from block-parsing code, the caller must
982 * match { before and } after.
984 static JSParseNode *
985 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
987 JSParseNode *pn, *pn2;
988 JSTokenType tt;
990 CHECK_RECURSION();
992 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
993 if (!pn)
994 return NULL;
995 PN_INIT_LIST(pn);
997 ts->flags |= TSF_REGEXP;
998 while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
999 ts->flags &= ~TSF_REGEXP;
1000 pn2 = Statement(cx, ts, tc);
1001 if (!pn2)
1002 return NULL;
1003 ts->flags |= TSF_REGEXP;
1005 /* If compiling top-level statements, emit as we go to save space. */
1006 if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1007 if (cx->fp->fun &&
1008 JS_HAS_STRICT_OPTION(cx) &&
1009 (tc->flags & TCF_RETURN_EXPR)) {
1011 * Check pn2 for lack of a final return statement if it is the
1012 * last statement in the block.
1014 tt = js_PeekToken(cx, ts);
1015 if ((tt == TOK_EOF || tt == TOK_RC) &&
1016 !CheckFinalReturn(cx, ts, pn2)) {
1017 tt = TOK_ERROR;
1018 break;
1022 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1023 * CheckFinalReturn again.
1025 tc->flags &= ~TCF_RETURN_EXPR;
1027 if (!js_FoldConstants(cx, pn2, tc) ||
1028 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1029 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1030 tt = TOK_ERROR;
1031 break;
1033 RecycleTree(pn2, tc);
1034 } else {
1035 PN_APPEND(pn, pn2);
1038 ts->flags &= ~TSF_REGEXP;
1039 if (tt == TOK_ERROR)
1040 return NULL;
1042 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1043 return pn;
1046 static JSParseNode *
1047 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1049 JSParseNode *pn, *pn2;
1051 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1052 pn = Expr(cx, ts, tc);
1053 if (!pn)
1054 return NULL;
1055 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1058 * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1059 * greater precedence than ==.
1060 * XXX not ECMA, but documented in several books -- now a strict warning.
1062 if (pn->pn_type == TOK_ASSIGN &&
1063 pn->pn_op == JSOP_NOP &&
1064 pn->pn_right->pn_type > TOK_EQOP)
1066 JSBool rewrite = !JSVERSION_IS_ECMA(cx->version);
1067 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1068 JSREPORT_WARNING | JSREPORT_STRICT,
1069 JSMSG_EQUAL_AS_ASSIGN,
1070 rewrite
1071 ? "\nAssuming equality test"
1072 : "")) {
1073 return NULL;
1075 if (rewrite) {
1076 pn->pn_type = TOK_EQOP;
1077 pn->pn_op = (JSOp)cx->jsop_eq;
1078 pn2 = pn->pn_left;
1079 switch (pn2->pn_op) {
1080 case JSOP_SETNAME:
1081 pn2->pn_op = JSOP_NAME;
1082 break;
1083 case JSOP_SETPROP:
1084 pn2->pn_op = JSOP_GETPROP;
1085 break;
1086 case JSOP_SETELEM:
1087 pn2->pn_op = JSOP_GETELEM;
1088 break;
1089 default:
1090 JS_ASSERT(0);
1094 return pn;
1097 static JSBool
1098 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1100 JSAtom *label;
1101 #if JS_HAS_LABEL_STATEMENT
1102 JSTokenType tt;
1104 tt = js_PeekTokenSameLine(cx, ts);
1105 if (tt == TOK_ERROR)
1106 return JS_FALSE;
1107 if (tt == TOK_NAME) {
1108 (void) js_GetToken(cx, ts);
1109 label = CURRENT_TOKEN(ts).t_atom;
1110 } else {
1111 label = NULL;
1113 #else
1114 label = NULL;
1115 #endif
1116 pn->pn_atom = label;
1117 return JS_TRUE;
1120 #if JS_HAS_EXPORT_IMPORT
1121 static JSParseNode *
1122 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1124 JSParseNode *pn, *pn2, *pn3;
1125 JSTokenType tt;
1127 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1128 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1129 if (!pn)
1130 return NULL;
1131 pn->pn_op = JSOP_NAME;
1132 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1133 pn->pn_expr = NULL;
1134 pn->pn_slot = -1;
1135 pn->pn_attrs = 0;
1137 ts->flags |= TSF_REGEXP;
1138 while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1139 ts->flags &= ~TSF_REGEXP;
1140 if (pn->pn_op == JSOP_IMPORTALL)
1141 goto bad_import;
1143 if (tt == TOK_DOT) {
1144 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1145 if (!pn2)
1146 return NULL;
1147 if (js_MatchToken(cx, ts, TOK_STAR)) {
1148 pn2->pn_op = JSOP_IMPORTALL;
1149 pn2->pn_atom = NULL;
1150 } else {
1151 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1152 pn2->pn_op = JSOP_GETPROP;
1153 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1154 pn2->pn_slot = -1;
1155 pn2->pn_attrs = 0;
1157 pn2->pn_expr = pn;
1158 pn2->pn_pos.begin = pn->pn_pos.begin;
1159 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1160 } else {
1161 /* Make a TOK_LB node. */
1162 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1163 if (!pn2)
1164 return NULL;
1165 pn3 = Expr(cx, ts, tc);
1166 if (!pn3)
1167 return NULL;
1169 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1170 pn2->pn_pos.begin = pn->pn_pos.begin;
1171 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1173 pn2->pn_op = JSOP_GETELEM;
1174 pn2->pn_left = pn;
1175 pn2->pn_right = pn3;
1178 pn = pn2;
1179 ts->flags |= TSF_REGEXP;
1181 ts->flags &= ~TSF_REGEXP;
1182 if (tt == TOK_ERROR)
1183 return NULL;
1184 js_UngetToken(ts);
1186 switch (pn->pn_op) {
1187 case JSOP_GETPROP:
1188 pn->pn_op = JSOP_IMPORTPROP;
1189 break;
1190 case JSOP_GETELEM:
1191 pn->pn_op = JSOP_IMPORTELEM;
1192 break;
1193 case JSOP_IMPORTALL:
1194 break;
1195 default:
1196 goto bad_import;
1198 return pn;
1200 bad_import:
1201 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT);
1202 return NULL;
1204 #endif /* JS_HAS_EXPORT_IMPORT */
1206 extern const char js_with_statement_str[];
1208 static JSParseNode *
1209 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1211 JSTokenType tt;
1212 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1213 JSStmtInfo stmtInfo, *stmt, *stmt2;
1214 JSAtom *label;
1216 CHECK_RECURSION();
1218 ts->flags |= TSF_REGEXP;
1219 tt = js_GetToken(cx, ts);
1220 ts->flags &= ~TSF_REGEXP;
1222 #if JS_HAS_GETTER_SETTER
1223 if (tt == TOK_NAME) {
1224 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1225 if (tt == TOK_ERROR)
1226 return NULL;
1228 #endif
1230 switch (tt) {
1231 #if JS_HAS_EXPORT_IMPORT
1232 case TOK_EXPORT:
1233 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1234 if (!pn)
1235 return NULL;
1236 PN_INIT_LIST(pn);
1237 if (js_MatchToken(cx, ts, TOK_STAR)) {
1238 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1239 if (!pn2)
1240 return NULL;
1241 PN_APPEND(pn, pn2);
1242 } else {
1243 do {
1244 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1245 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1246 if (!pn2)
1247 return NULL;
1248 pn2->pn_op = JSOP_NAME;
1249 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1250 pn2->pn_expr = NULL;
1251 pn2->pn_slot = -1;
1252 pn2->pn_attrs = 0;
1253 PN_APPEND(pn, pn2);
1254 } while (js_MatchToken(cx, ts, TOK_COMMA));
1256 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1257 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1258 break;
1260 case TOK_IMPORT:
1261 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1262 if (!pn)
1263 return NULL;
1264 PN_INIT_LIST(pn);
1265 do {
1266 pn2 = ImportExpr(cx, ts, tc);
1267 if (!pn2)
1268 return NULL;
1269 PN_APPEND(pn, pn2);
1270 } while (js_MatchToken(cx, ts, TOK_COMMA));
1271 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1272 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1273 break;
1274 #endif /* JS_HAS_EXPORT_IMPORT */
1276 case TOK_FUNCTION:
1277 return FunctionStmt(cx, ts, tc);
1279 case TOK_IF:
1280 /* An IF node has three kids: condition, then, and optional else. */
1281 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1282 if (!pn)
1283 return NULL;
1284 pn1 = Condition(cx, ts, tc);
1285 if (!pn1)
1286 return NULL;
1287 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1288 pn2 = Statement(cx, ts, tc);
1289 if (!pn2)
1290 return NULL;
1291 if (js_MatchToken(cx, ts, TOK_ELSE)) {
1292 stmtInfo.type = STMT_ELSE;
1293 pn3 = Statement(cx, ts, tc);
1294 if (!pn3)
1295 return NULL;
1296 pn->pn_pos.end = pn3->pn_pos.end;
1297 } else {
1298 pn3 = NULL;
1299 pn->pn_pos.end = pn2->pn_pos.end;
1301 js_PopStatement(tc);
1302 pn->pn_kid1 = pn1;
1303 pn->pn_kid2 = pn2;
1304 pn->pn_kid3 = pn3;
1305 return pn;
1307 #if JS_HAS_SWITCH_STATEMENT
1308 case TOK_SWITCH:
1310 JSParseNode *pn5;
1311 JSBool seenDefault = JS_FALSE;
1313 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1314 if (!pn)
1315 return NULL;
1316 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1318 /* pn1 points to the switch's discriminant. */
1319 pn1 = Expr(cx, ts, tc);
1320 if (!pn1)
1321 return NULL;
1323 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1324 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1326 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1327 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1328 if (!pn2)
1329 return NULL;
1330 PN_INIT_LIST(pn2);
1332 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1334 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1335 switch (tt) {
1336 case TOK_DEFAULT:
1337 if (seenDefault) {
1338 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1339 JSMSG_TOO_MANY_DEFAULTS);
1340 return NULL;
1342 seenDefault = JS_TRUE;
1343 /* fall through */
1345 case TOK_CASE:
1346 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1347 if (!pn3)
1348 return NULL;
1349 if (tt == TOK_DEFAULT) {
1350 pn3->pn_left = NULL;
1351 } else {
1352 pn3->pn_left = Expr(cx, ts, tc);
1353 if (!pn3->pn_left)
1354 return NULL;
1356 PN_APPEND(pn2, pn3);
1357 if (pn2->pn_count == JS_BIT(16)) {
1358 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1359 JSMSG_TOO_MANY_CASES);
1360 return NULL;
1362 break;
1364 case TOK_ERROR:
1365 return NULL;
1367 default:
1368 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1369 JSMSG_BAD_SWITCH);
1370 return NULL;
1372 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1374 pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1375 if (!pn4)
1376 return NULL;
1377 pn4->pn_type = TOK_LC;
1378 PN_INIT_LIST(pn4);
1379 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1380 tt != TOK_CASE && tt != TOK_DEFAULT) {
1381 if (tt == TOK_ERROR)
1382 return NULL;
1383 pn5 = Statement(cx, ts, tc);
1384 if (!pn5)
1385 return NULL;
1386 pn4->pn_pos.end = pn5->pn_pos.end;
1387 PN_APPEND(pn4, pn5);
1390 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1391 if (pn4->pn_head)
1392 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1393 pn3->pn_pos.end = pn4->pn_pos.end;
1394 pn3->pn_right = pn4;
1397 js_PopStatement(tc);
1399 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1400 pn->pn_kid1 = pn1;
1401 pn->pn_kid2 = pn2;
1402 return pn;
1404 #endif /* JS_HAS_SWITCH_STATEMENT */
1406 case TOK_WHILE:
1407 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1408 if (!pn)
1409 return NULL;
1410 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1411 pn2 = Condition(cx, ts, tc);
1412 if (!pn2)
1413 return NULL;
1414 pn->pn_left = pn2;
1415 pn2 = Statement(cx, ts, tc);
1416 if (!pn2)
1417 return NULL;
1418 js_PopStatement(tc);
1419 pn->pn_pos.end = pn2->pn_pos.end;
1420 pn->pn_right = pn2;
1421 return pn;
1423 #if JS_HAS_DO_WHILE_LOOP
1424 case TOK_DO:
1425 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1426 if (!pn)
1427 return NULL;
1428 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1429 pn2 = Statement(cx, ts, tc);
1430 if (!pn2)
1431 return NULL;
1432 pn->pn_left = pn2;
1433 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1434 pn2 = Condition(cx, ts, tc);
1435 if (!pn2)
1436 return NULL;
1437 js_PopStatement(tc);
1438 pn->pn_pos.end = pn2->pn_pos.end;
1439 pn->pn_right = pn2;
1440 if (cx->version != JSVERSION_ECMA_3) {
1442 * All legacy and extended versions must do automatic semicolon
1443 * insertion after do-while. See the testcase and discussion in
1444 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
1446 (void) js_MatchToken(cx, ts, TOK_SEMI);
1447 return pn;
1449 break;
1450 #endif /* JS_HAS_DO_WHILE_LOOP */
1452 case TOK_FOR:
1453 /* A FOR node is binary, left is loop control and right is the body. */
1454 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1455 if (!pn)
1456 return NULL;
1457 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1459 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1460 ts->flags |= TSF_REGEXP;
1461 tt = js_PeekToken(cx, ts);
1462 ts->flags &= ~TSF_REGEXP;
1463 if (tt == TOK_SEMI) {
1464 /* No initializer -- set first kid of left sub-node to null. */
1465 pn1 = NULL;
1466 } else {
1467 /* Set pn1 to a var list or an initializing expression. */
1468 #if JS_HAS_IN_OPERATOR
1470 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1471 * of the for statement. This flag will be used by the RelExpr
1472 * production; if it is set, then the 'in' keyword will not be
1473 * recognized as an operator, leaving it available to be parsed as
1474 * part of a for/in loop. A side effect of this restriction is
1475 * that (unparenthesized) expressions involving an 'in' operator
1476 * are illegal in the init clause of an ordinary for loop.
1478 tc->flags |= TCF_IN_FOR_INIT;
1479 #endif /* JS_HAS_IN_OPERATOR */
1480 if (tt == TOK_VAR) {
1481 (void) js_GetToken(cx, ts);
1482 pn1 = Variables(cx, ts, tc);
1483 } else {
1484 pn1 = Expr(cx, ts, tc);
1486 #if JS_HAS_IN_OPERATOR
1487 tc->flags &= ~TCF_IN_FOR_INIT;
1488 #endif /* JS_HAS_IN_OPERATOR */
1489 if (!pn1)
1490 return NULL;
1494 * We can be sure that it's a for/in loop if there's still an 'in'
1495 * keyword here, even if JavaScript recognizes 'in' as an operator,
1496 * as we've excluded 'in' from being parsed in RelExpr by setting
1497 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
1499 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1500 stmtInfo.type = STMT_FOR_IN_LOOP;
1502 /* Check that the left side of the 'in' is valid. */
1503 if ((pn1->pn_type == TOK_VAR)
1504 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
1505 : (pn1->pn_type != TOK_NAME &&
1506 pn1->pn_type != TOK_DOT &&
1507 pn1->pn_type != TOK_LB)) {
1508 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1509 JSMSG_BAD_FOR_LEFTSIDE);
1510 return NULL;
1513 if (pn1->pn_type == TOK_VAR) {
1514 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
1515 pn1->pn_extra |= PNX_FORINVAR;
1517 /* Generate a final POP only if the var has an initializer. */
1518 pn2 = pn1->pn_head;
1519 if (pn2->pn_expr)
1520 pn1->pn_extra |= PNX_POPVAR;
1521 } else {
1522 pn2 = pn1;
1525 /* Beware 'for (arguments in ...)' with or without a 'var'. */
1526 if (pn2->pn_type == TOK_NAME &&
1527 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1528 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1531 /* Parse the object expression as the right operand of 'in'. */
1532 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1533 if (!pn2)
1534 return NULL;
1535 pn->pn_left = pn2;
1536 } else {
1537 /* Parse the loop condition or null into pn2. */
1538 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1539 ts->flags |= TSF_REGEXP;
1540 tt = js_PeekToken(cx, ts);
1541 ts->flags &= ~TSF_REGEXP;
1542 if (tt == TOK_SEMI) {
1543 pn2 = NULL;
1544 } else {
1545 pn2 = Expr(cx, ts, tc);
1546 if (!pn2)
1547 return NULL;
1550 /* Parse the update expression or null into pn3. */
1551 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1552 ts->flags |= TSF_REGEXP;
1553 tt = js_PeekToken(cx, ts);
1554 ts->flags &= ~TSF_REGEXP;
1555 if (tt == TOK_RP) {
1556 pn3 = NULL;
1557 } else {
1558 pn3 = Expr(cx, ts, tc);
1559 if (!pn3)
1560 return NULL;
1563 /* Build the RESERVED node to use as the left kid of pn. */
1564 pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1565 if (!pn4)
1566 return NULL;
1567 pn4->pn_type = TOK_RESERVED;
1568 pn4->pn_op = JSOP_NOP;
1569 pn4->pn_kid1 = pn1;
1570 pn4->pn_kid2 = pn2;
1571 pn4->pn_kid3 = pn3;
1572 pn->pn_left = pn4;
1575 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1577 /* Parse the loop body into pn->pn_right. */
1578 pn2 = Statement(cx, ts, tc);
1579 if (!pn2)
1580 return NULL;
1581 pn->pn_right = pn2;
1582 js_PopStatement(tc);
1584 /* Record the absolute line number for source note emission. */
1585 pn->pn_pos.end = pn2->pn_pos.end;
1586 return pn;
1588 #if JS_HAS_EXCEPTIONS
1589 case TOK_TRY: {
1590 JSParseNode *catchtail = NULL;
1592 * try nodes are ternary.
1593 * kid1 is the try Statement
1594 * kid2 is the catch node
1595 * kid3 is the finally Statement
1597 * catch nodes are ternary.
1598 * kid1 is the discriminant
1599 * kid2 is the next catch node, or NULL
1600 * kid3 is the catch block (on kid3 so that we can always append a
1601 * new catch pn on catchtail->kid2)
1603 * catch discriminant nodes are binary
1604 * atom is the receptacle
1605 * expr is the discriminant code
1607 * finally nodes are unary (just the finally expression)
1609 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1610 pn->pn_op = JSOP_NOP;
1612 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1613 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1614 pn->pn_kid1 = Statements(cx, ts, tc);
1615 if (!pn->pn_kid1)
1616 return NULL;
1617 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1618 js_PopStatement(tc);
1620 catchtail = pn;
1621 while (js_PeekToken(cx, ts) == TOK_CATCH) {
1622 /* check for another catch after unconditional catch */
1623 if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1624 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1625 JSMSG_CATCH_AFTER_GENERAL);
1626 return NULL;
1630 * legal catch forms are:
1631 * catch (v)
1632 * catch (v if <boolean_expression>)
1633 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1635 (void) js_GetToken(cx, ts); /* eat `catch' */
1636 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1637 if (!pn2)
1638 return NULL;
1641 * We use a PN_NAME for the discriminant (catchguard) node
1642 * with the actual discriminant code in the initializer spot
1644 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1645 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1646 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1647 if (!pn3)
1648 return NULL;
1650 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1651 pn3->pn_expr = NULL;
1652 #if JS_HAS_CATCH_GUARD
1654 * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1655 * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1657 if (js_PeekToken(cx, ts) == TOK_IF) {
1658 (void)js_GetToken(cx, ts); /* eat `if' */
1659 pn3->pn_expr = Expr(cx, ts, tc);
1660 if (!pn3->pn_expr)
1661 return NULL;
1663 #endif
1664 pn2->pn_kid1 = pn3;
1666 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1668 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1669 js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1670 stmtInfo.label = pn3->pn_atom;
1671 pn2->pn_kid3 = Statements(cx, ts, tc);
1672 if (!pn2->pn_kid3)
1673 return NULL;
1674 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1675 js_PopStatement(tc);
1677 catchtail = catchtail->pn_kid2 = pn2;
1679 catchtail->pn_kid2 = NULL;
1681 if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1682 tc->tryCount++;
1683 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1684 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1685 pn->pn_kid3 = Statements(cx, ts, tc);
1686 if (!pn->pn_kid3)
1687 return NULL;
1688 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1689 js_PopStatement(tc);
1690 } else {
1691 pn->pn_kid3 = NULL;
1693 if (!pn->pn_kid2 && !pn->pn_kid3) {
1694 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1695 JSMSG_CATCH_OR_FINALLY);
1696 return NULL;
1698 tc->tryCount++;
1699 return pn;
1702 case TOK_THROW:
1703 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1704 if (!pn)
1705 return NULL;
1706 pn2 = Expr(cx, ts, tc);
1707 if (!pn2)
1708 return NULL;
1709 pn->pn_pos.end = pn2->pn_pos.end;
1710 pn->pn_op = JSOP_THROW;
1711 pn->pn_kid = pn2;
1712 break;
1714 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1715 case TOK_CATCH:
1716 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1717 JSMSG_CATCH_WITHOUT_TRY);
1718 return NULL;
1720 case TOK_FINALLY:
1721 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1722 JSMSG_FINALLY_WITHOUT_TRY);
1723 return NULL;
1725 #endif /* JS_HAS_EXCEPTIONS */
1727 case TOK_BREAK:
1728 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1729 if (!pn)
1730 return NULL;
1731 if (!MatchLabel(cx, ts, pn))
1732 return NULL;
1733 stmt = tc->topStmt;
1734 label = pn->pn_atom;
1735 if (label) {
1736 for (; ; stmt = stmt->down) {
1737 if (!stmt) {
1738 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1739 JSMSG_LABEL_NOT_FOUND);
1740 return NULL;
1742 if (stmt->type == STMT_LABEL && stmt->label == label)
1743 break;
1745 } else {
1746 for (; ; stmt = stmt->down) {
1747 if (!stmt) {
1748 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1749 JSMSG_TOUGH_BREAK);
1750 return NULL;
1752 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1753 break;
1756 if (label)
1757 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1758 break;
1760 case TOK_CONTINUE:
1761 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1762 if (!pn)
1763 return NULL;
1764 if (!MatchLabel(cx, ts, pn))
1765 return NULL;
1766 stmt = tc->topStmt;
1767 label = pn->pn_atom;
1768 if (label) {
1769 for (stmt2 = NULL; ; stmt = stmt->down) {
1770 if (!stmt) {
1771 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1772 JSMSG_LABEL_NOT_FOUND);
1773 return NULL;
1775 if (stmt->type == STMT_LABEL) {
1776 if (stmt->label == label) {
1777 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1778 js_ReportCompileErrorNumber(cx, ts, NULL,
1779 JSREPORT_ERROR,
1780 JSMSG_BAD_CONTINUE);
1781 return NULL;
1783 break;
1785 } else {
1786 stmt2 = stmt;
1789 } else {
1790 for (; ; stmt = stmt->down) {
1791 if (!stmt) {
1792 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1793 JSMSG_BAD_CONTINUE);
1794 return NULL;
1796 if (STMT_IS_LOOP(stmt))
1797 break;
1800 if (label)
1801 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1802 break;
1804 case TOK_WITH:
1805 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1806 if (!pn)
1807 return NULL;
1808 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1809 pn2 = Expr(cx, ts, tc);
1810 if (!pn2)
1811 return NULL;
1812 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1813 pn->pn_left = pn2;
1815 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1816 pn2 = Statement(cx, ts, tc);
1817 if (!pn2)
1818 return NULL;
1819 js_PopStatement(tc);
1821 /* Deprecate after parsing, in case of WERROR option. */
1822 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1823 JSREPORT_WARNING | JSREPORT_STRICT,
1824 JSMSG_DEPRECATED_USAGE,
1825 js_with_statement_str)) {
1826 return NULL;
1829 pn->pn_pos.end = pn2->pn_pos.end;
1830 pn->pn_right = pn2;
1831 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1832 return pn;
1834 case TOK_VAR:
1835 pn = Variables(cx, ts, tc);
1836 if (!pn)
1837 return NULL;
1839 /* Tell js_EmitTree to generate a final POP. */
1840 pn->pn_extra |= PNX_POPVAR;
1841 break;
1843 case TOK_RETURN:
1844 if (!(tc->flags & TCF_IN_FUNCTION)) {
1845 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1846 JSMSG_BAD_RETURN);
1847 return NULL;
1849 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1850 if (!pn)
1851 return NULL;
1853 /* This is ugly, but we don't want to require a semicolon. */
1854 ts->flags |= TSF_REGEXP;
1855 tt = js_PeekTokenSameLine(cx, ts);
1856 ts->flags &= ~TSF_REGEXP;
1857 if (tt == TOK_ERROR)
1858 return NULL;
1860 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1861 pn2 = Expr(cx, ts, tc);
1862 if (!pn2)
1863 return NULL;
1864 tc->flags |= TCF_RETURN_EXPR;
1865 pn->pn_pos.end = pn2->pn_pos.end;
1866 pn->pn_kid = pn2;
1867 } else {
1868 tc->flags |= TCF_RETURN_VOID;
1869 pn->pn_kid = NULL;
1872 if (JS_HAS_STRICT_OPTION(cx) &&
1873 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
1875 * We must be in a frame with a non-native function, because
1876 * we're compiling one.
1878 if (!ReportNoReturnValue(cx, ts))
1879 return NULL;
1881 break;
1883 case TOK_LC:
1884 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1885 pn = Statements(cx, ts, tc);
1886 if (!pn)
1887 return NULL;
1889 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
1890 js_PopStatement(tc);
1891 return pn;
1893 case TOK_EOL:
1894 case TOK_SEMI:
1895 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1896 if (!pn)
1897 return NULL;
1898 pn->pn_type = TOK_SEMI;
1899 pn->pn_kid = NULL;
1900 return pn;
1902 #if JS_HAS_DEBUGGER_KEYWORD
1903 case TOK_DEBUGGER:
1904 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1905 if (!pn)
1906 return NULL;
1907 pn->pn_type = TOK_DEBUGGER;
1908 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1909 break;
1910 #endif /* JS_HAS_DEBUGGER_KEYWORD */
1912 case TOK_ERROR:
1913 return NULL;
1915 default:
1916 js_UngetToken(ts);
1917 pn2 = Expr(cx, ts, tc);
1918 if (!pn2)
1919 return NULL;
1921 if (js_PeekToken(cx, ts) == TOK_COLON) {
1922 if (pn2->pn_type != TOK_NAME) {
1923 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1924 JSMSG_BAD_LABEL);
1925 return NULL;
1927 label = pn2->pn_atom;
1928 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1929 if (stmt->type == STMT_LABEL && stmt->label == label) {
1930 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1931 JSMSG_DUPLICATE_LABEL);
1932 return NULL;
1935 (void) js_GetToken(cx, ts);
1937 /* Push a label struct and parse the statement. */
1938 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
1939 stmtInfo.label = label;
1940 pn = Statement(cx, ts, tc);
1941 if (!pn)
1942 return NULL;
1944 /* Pop the label, set pn_expr, and return early. */
1945 js_PopStatement(tc);
1946 pn2->pn_type = TOK_COLON;
1947 pn2->pn_pos.end = pn->pn_pos.end;
1948 pn2->pn_expr = pn;
1949 return pn2;
1952 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1953 if (!pn)
1954 return NULL;
1955 pn->pn_type = TOK_SEMI;
1956 pn->pn_pos = pn2->pn_pos;
1957 pn->pn_kid = pn2;
1958 break;
1961 /* Check termination of this primitive statement. */
1962 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
1963 tt = js_PeekTokenSameLine(cx, ts);
1964 if (tt == TOK_ERROR)
1965 return NULL;
1966 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1967 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1968 JSMSG_SEMI_BEFORE_STMNT);
1969 return NULL;
1973 (void) js_MatchToken(cx, ts, TOK_SEMI);
1974 return pn;
1977 static JSParseNode *
1978 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1980 JSParseNode *pn, *pn2;
1981 JSObject *obj, *pobj;
1982 JSStackFrame *fp;
1983 JSFunction *fun;
1984 JSClass *clasp;
1985 JSPropertyOp getter, setter, currentGetter, currentSetter;
1986 JSAtom *atom;
1987 JSAtomListElement *ale;
1988 JSOp prevop;
1989 JSProperty *prop;
1990 JSScopeProperty *sprop;
1991 JSBool ok;
1994 * The tricky part of this code is to create special parsenode opcodes for
1995 * getting and setting variables (which will be stored as special slots in
1996 * the frame). The complex special case is an eval() inside a function.
1997 * If the evaluated string references variables in the enclosing function,
1998 * then we need to generate the special variable opcodes. We determine
1999 * this by looking up the variable id in the current variable scope.
2001 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
2002 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2003 if (!pn)
2004 return NULL;
2005 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2006 pn->pn_extra = 0; /* assume no JSOP_POP needed */
2007 PN_INIT_LIST(pn);
2010 * Skip eval and debugger frames when looking for the function whose code
2011 * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION
2012 * will be set in tc->flags, and we can be sure fp->fun is the function to
2013 * use. But if a function calls eval, the string argument is treated as a
2014 * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
2016 * What's more, when the following code is reached from eval, cx->fp->fun
2017 * is eval's JSFunction (a native function), so we need to skip its frame.
2018 * We should find the scripted caller's function frame just below it, but
2019 * we code a loop out of paranoia.
2021 for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
2022 continue;
2023 obj = fp->varobj;
2024 fun = fp->fun;
2025 clasp = OBJ_GET_CLASS(cx, obj);
2026 if (fun && clasp == &js_FunctionClass) {
2027 /* We are compiling code inside a function */
2028 getter = js_GetLocalVariable;
2029 setter = js_SetLocalVariable;
2030 } else if (fun && clasp == &js_CallClass) {
2031 /* We are compiling code from an eval inside a function */
2032 getter = js_GetCallVariable;
2033 setter = js_SetCallVariable;
2034 } else {
2035 getter = clasp->getProperty;
2036 setter = clasp->setProperty;
2039 ok = JS_TRUE;
2040 do {
2041 currentGetter = getter;
2042 currentSetter = setter;
2043 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2044 atom = CURRENT_TOKEN(ts).t_atom;
2046 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2047 if (ale) {
2048 prevop = ALE_JSOP(ale);
2049 if (JS_HAS_STRICT_OPTION(cx) ||
2050 pn->pn_op == JSOP_DEFCONST ||
2051 prevop == JSOP_DEFCONST) {
2052 const char *name = js_AtomToPrintableString(cx, atom);
2053 if (!name ||
2054 !js_ReportCompileErrorNumber(cx, ts, NULL,
2055 (pn->pn_op != JSOP_DEFCONST &&
2056 prevop != JSOP_DEFCONST)
2057 ? JSREPORT_WARNING |
2058 JSREPORT_STRICT
2059 : JSREPORT_ERROR,
2060 JSMSG_REDECLARED_VAR,
2061 (prevop == JSOP_DEFFUN ||
2062 prevop == JSOP_CLOSURE)
2063 ? js_function_str
2064 : (prevop == JSOP_DEFCONST)
2065 ? js_const_str
2066 : js_var_str,
2067 name)) {
2068 return NULL;
2071 if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2072 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2073 } else {
2074 ale = js_IndexAtom(cx, atom, &tc->decls);
2075 if (!ale)
2076 return NULL;
2078 ALE_SET_JSOP(ale, pn->pn_op);
2080 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2081 if (!pn2)
2082 return NULL;
2083 pn2->pn_op = JSOP_NAME;
2084 pn2->pn_atom = atom;
2085 pn2->pn_expr = NULL;
2086 pn2->pn_slot = -1;
2087 pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2088 ? JSPROP_ENUMERATE | JSPROP_PERMANENT |
2089 JSPROP_READONLY
2090 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2091 PN_APPEND(pn, pn2);
2093 if (!fun) {
2094 prop = NULL; /* don't lookup global variables at compile time */
2095 } else {
2096 if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
2097 return NULL;
2099 if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2100 sprop = (JSScopeProperty *)prop;
2101 if (sprop->getter == js_GetArgument) {
2102 const char *name = js_AtomToPrintableString(cx, atom);
2103 if (!name) {
2104 ok = JS_FALSE;
2105 } else if (pn->pn_op == JSOP_DEFCONST) {
2106 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2107 JSMSG_REDECLARED_PARAM,
2108 name);
2109 ok = JS_FALSE;
2110 } else {
2111 currentGetter = js_GetArgument;
2112 currentSetter = js_SetArgument;
2113 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
2114 JSREPORT_WARNING |
2115 JSREPORT_STRICT,
2116 JSMSG_VAR_HIDES_ARG,
2117 name);
2119 } else {
2120 if (fun) {
2121 /* Not an argument, must be a redeclared local var. */
2122 if (clasp == &js_FunctionClass) {
2123 JS_ASSERT(sprop->getter == js_GetLocalVariable);
2124 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2125 sprop->shortid < fun->nvars);
2126 } else if (clasp == &js_CallClass) {
2127 if (sprop->getter == js_GetCallVariable) {
2129 * Referencing a variable introduced by a var
2130 * statement in the enclosing function. Check
2131 * that the slot number we have is in range.
2133 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2134 sprop->shortid < fun->nvars);
2135 } else {
2137 * A variable introduced through another eval:
2138 * don't use the special getters and setters
2139 * since we can't allocate a slot in the frame.
2141 currentGetter = sprop->getter;
2142 currentSetter = sprop->setter;
2146 /* Override the old getter and setter, to handle eval. */
2147 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2148 0, sprop->attrs,
2149 currentGetter,
2150 currentSetter);
2151 if (!sprop)
2152 ok = JS_FALSE;
2155 } else {
2157 * Property not found in current variable scope: we have not seen
2158 * this variable before. Define a new local variable by adding a
2159 * property to the function's scope, allocating one slot in the
2160 * function's frame. Global variables and any locals declared in
2161 * with statement bodies are handled at runtime, by script prolog
2162 * JSOP_DEFVAR bytecodes generated for slot-less vars.
2164 sprop = NULL;
2165 if (prop) {
2166 OBJ_DROP_PROPERTY(cx, pobj, prop);
2167 prop = NULL;
2169 if (currentGetter == js_GetCallVariable) {
2170 /* Can't increase fun->nvars in an active frame! */
2171 currentGetter = clasp->getProperty;
2172 currentSetter = clasp->setProperty;
2174 if (currentGetter == js_GetLocalVariable &&
2175 atom != cx->runtime->atomState.argumentsAtom &&
2176 fp->scopeChain == obj &&
2177 !js_InWithStatement(tc)) {
2178 if (!js_AddNativeProperty(cx, obj, (jsid)atom,
2179 currentGetter, currentSetter,
2180 SPROP_INVALID_SLOT,
2181 pn2->pn_attrs | JSPROP_SHARED,
2182 SPROP_HAS_SHORTID, fun->nvars)) {
2183 ok = JS_FALSE;
2185 fun->nvars++;
2189 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2190 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2191 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2192 JSMSG_BAD_VAR_INIT);
2193 ok = JS_FALSE;
2194 } else {
2195 pn2->pn_expr = AssignExpr(cx, ts, tc);
2196 if (!pn2->pn_expr) {
2197 ok = JS_FALSE;
2198 } else {
2199 pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2200 ? JSOP_SETCONST
2201 : JSOP_SETNAME;
2202 if (atom == cx->runtime->atomState.argumentsAtom)
2203 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2208 if (prop)
2209 OBJ_DROP_PROPERTY(cx, pobj, prop);
2210 if (!ok)
2211 return NULL;
2212 } while (js_MatchToken(cx, ts, TOK_COMMA));
2214 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2215 return pn;
2218 static JSParseNode *
2219 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2221 JSParseNode *pn, *pn2;
2223 pn = AssignExpr(cx, ts, tc);
2224 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2225 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2226 if (!pn2)
2227 return NULL;
2228 pn2->pn_pos.begin = pn->pn_pos.begin;
2229 PN_INIT_LIST_1(pn2, pn);
2230 pn = pn2;
2231 do {
2232 pn2 = AssignExpr(cx, ts, tc);
2233 if (!pn2)
2234 return NULL;
2235 PN_APPEND(pn, pn2);
2236 } while (js_MatchToken(cx, ts, TOK_COMMA));
2237 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2239 return pn;
2242 static JSParseNode *
2243 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2245 JSParseNode *pn, *pn2;
2246 JSTokenType tt;
2247 JSOp op;
2249 CHECK_RECURSION();
2251 pn = CondExpr(cx, ts, tc);
2252 if (!pn)
2253 return NULL;
2255 tt = js_GetToken(cx, ts);
2256 #if JS_HAS_GETTER_SETTER
2257 if (tt == TOK_NAME) {
2258 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2259 if (tt == TOK_ERROR)
2260 return NULL;
2262 #endif
2263 if (tt != TOK_ASSIGN) {
2264 js_UngetToken(ts);
2265 return pn;
2268 op = CURRENT_TOKEN(ts).t_op;
2269 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2270 continue;
2271 switch (pn2->pn_type) {
2272 case TOK_NAME:
2273 pn2->pn_op = JSOP_SETNAME;
2274 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2275 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2276 break;
2277 case TOK_DOT:
2278 pn2->pn_op = JSOP_SETPROP;
2279 break;
2280 case TOK_LB:
2281 pn2->pn_op = JSOP_SETELEM;
2282 break;
2283 #if JS_HAS_LVALUE_RETURN
2284 case TOK_LP:
2285 pn2->pn_op = JSOP_SETCALL;
2286 break;
2287 #endif
2288 default:
2289 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2290 JSMSG_BAD_LEFTSIDE_OF_ASS);
2291 return NULL;
2293 pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2294 return pn;
2297 static JSParseNode *
2298 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2300 JSParseNode *pn, *pn1, *pn2, *pn3;
2301 #if JS_HAS_IN_OPERATOR
2302 uintN oldflags;
2303 #endif /* JS_HAS_IN_OPERATOR */
2305 pn = OrExpr(cx, ts, tc);
2306 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2307 pn1 = pn;
2308 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2309 if (!pn)
2310 return NULL;
2311 #if JS_HAS_IN_OPERATOR
2313 * Always accept the 'in' operator in the middle clause of a ternary,
2314 * where it's unambiguous, even if we might be parsing the init of a
2315 * for statement.
2317 oldflags = tc->flags;
2318 tc->flags &= ~TCF_IN_FOR_INIT;
2319 #endif /* JS_HAS_IN_OPERATOR */
2320 pn2 = AssignExpr(cx, ts, tc);
2321 #if JS_HAS_IN_OPERATOR
2322 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2323 #endif /* JS_HAS_IN_OPERATOR */
2325 if (!pn2)
2326 return NULL;
2327 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2328 pn3 = AssignExpr(cx, ts, tc);
2329 if (!pn3)
2330 return NULL;
2331 pn->pn_pos.begin = pn1->pn_pos.begin;
2332 pn->pn_pos.end = pn3->pn_pos.end;
2333 pn->pn_kid1 = pn1;
2334 pn->pn_kid2 = pn2;
2335 pn->pn_kid3 = pn3;
2337 return pn;
2340 static JSParseNode *
2341 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2343 JSParseNode *pn;
2345 pn = AndExpr(cx, ts, tc);
2346 if (pn && js_MatchToken(cx, ts, TOK_OR))
2347 pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2348 return pn;
2351 static JSParseNode *
2352 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2354 JSParseNode *pn;
2356 pn = BitOrExpr(cx, ts, tc);
2357 if (pn && js_MatchToken(cx, ts, TOK_AND))
2358 pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2359 return pn;
2362 static JSParseNode *
2363 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2365 JSParseNode *pn;
2367 pn = BitXorExpr(cx, ts, tc);
2368 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2369 pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2370 tc);
2372 return pn;
2375 static JSParseNode *
2376 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2378 JSParseNode *pn;
2380 pn = BitAndExpr(cx, ts, tc);
2381 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2382 pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2383 tc);
2385 return pn;
2388 static JSParseNode *
2389 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2391 JSParseNode *pn;
2393 pn = EqExpr(cx, ts, tc);
2394 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2395 pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2396 return pn;
2399 static JSParseNode *
2400 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2402 JSParseNode *pn;
2403 JSOp op;
2405 pn = RelExpr(cx, ts, tc);
2406 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2407 op = CURRENT_TOKEN(ts).t_op;
2408 pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2410 return pn;
2413 static JSParseNode *
2414 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2416 JSParseNode *pn;
2417 JSTokenType tt;
2418 JSOp op;
2419 #if JS_HAS_IN_OPERATOR
2420 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2423 * Uses of the in operator in ShiftExprs are always unambiguous,
2424 * so unset the flag that prohibits recognizing it.
2426 tc->flags &= ~TCF_IN_FOR_INIT;
2427 #endif /* JS_HAS_IN_OPERATOR */
2429 pn = ShiftExpr(cx, ts, tc);
2430 while (pn &&
2431 (js_MatchToken(cx, ts, TOK_RELOP)
2432 #if JS_HAS_IN_OPERATOR
2434 * Recognize the 'in' token as an operator only if we're not
2435 * currently in the init expr of a for loop.
2437 || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
2438 #endif /* JS_HAS_IN_OPERATOR */
2439 #if JS_HAS_INSTANCEOF
2440 || js_MatchToken(cx, ts, TOK_INSTANCEOF)
2441 #endif /* JS_HAS_INSTANCEOF */
2442 )) {
2443 tt = CURRENT_TOKEN(ts).type;
2444 op = CURRENT_TOKEN(ts).t_op;
2445 pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
2447 #if JS_HAS_IN_OPERATOR
2448 /* Restore previous state of inForInit flag. */
2449 tc->flags |= inForInitFlag;
2450 #endif /* JS_HAS_IN_OPERATOR */
2452 return pn;
2455 static JSParseNode *
2456 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2458 JSParseNode *pn;
2459 JSOp op;
2461 pn = AddExpr(cx, ts, tc);
2462 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2463 op = CURRENT_TOKEN(ts).t_op;
2464 pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2466 return pn;
2469 static JSParseNode *
2470 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2472 JSParseNode *pn;
2473 JSTokenType tt;
2474 JSOp op;
2476 pn = MulExpr(cx, ts, tc);
2477 while (pn &&
2478 (js_MatchToken(cx, ts, TOK_PLUS) ||
2479 js_MatchToken(cx, ts, TOK_MINUS))) {
2480 tt = CURRENT_TOKEN(ts).type;
2481 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2482 pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2484 return pn;
2487 static JSParseNode *
2488 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2490 JSParseNode *pn;
2491 JSTokenType tt;
2492 JSOp op;
2494 pn = UnaryExpr(cx, ts, tc);
2495 while (pn &&
2496 (js_MatchToken(cx, ts, TOK_STAR) ||
2497 js_MatchToken(cx, ts, TOK_DIVOP))) {
2498 tt = CURRENT_TOKEN(ts).type;
2499 op = CURRENT_TOKEN(ts).t_op;
2500 pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2502 return pn;
2505 static JSParseNode *
2506 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2507 const char *name)
2509 while (kid->pn_type == TOK_RP)
2510 kid = kid->pn_kid;
2511 if (kid->pn_type != TOK_NAME &&
2512 kid->pn_type != TOK_DOT &&
2513 #if JS_HAS_LVALUE_RETURN
2514 (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
2515 #endif
2516 kid->pn_type != TOK_LB) {
2517 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2518 JSMSG_BAD_OPERAND, name);
2519 return NULL;
2521 pn->pn_kid = kid;
2522 return kid;
2525 static const char *incop_name_str[] = {"increment", "decrement"};
2527 static JSBool
2528 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2529 JSParseNode *pn, JSParseNode *kid,
2530 JSTokenType tt, JSBool preorder)
2532 JSOp op;
2534 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2535 if (!kid)
2536 return JS_FALSE;
2537 switch (kid->pn_type) {
2538 case TOK_NAME:
2539 op = (tt == TOK_INC)
2540 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2541 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2542 if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2543 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2544 break;
2546 case TOK_DOT:
2547 op = (tt == TOK_INC)
2548 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2549 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2550 break;
2552 #if JS_HAS_LVALUE_RETURN
2553 case TOK_LP:
2554 kid->pn_op = JSOP_SETCALL;
2555 #endif
2556 case TOK_LB:
2557 op = (tt == TOK_INC)
2558 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2559 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2560 break;
2562 default:
2563 JS_ASSERT(0);
2564 op = JSOP_NOP;
2566 pn->pn_op = op;
2567 return JS_TRUE;
2570 static JSParseNode *
2571 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2573 JSTokenType tt;
2574 JSParseNode *pn, *pn2;
2576 ts->flags |= TSF_REGEXP;
2577 tt = js_GetToken(cx, ts);
2578 ts->flags &= ~TSF_REGEXP;
2580 switch (tt) {
2581 case TOK_UNARYOP:
2582 case TOK_PLUS:
2583 case TOK_MINUS:
2584 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2585 if (!pn)
2586 return NULL;
2587 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
2588 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2589 pn2 = UnaryExpr(cx, ts, tc);
2590 if (!pn2)
2591 return NULL;
2592 pn->pn_pos.end = pn2->pn_pos.end;
2593 pn->pn_kid = pn2;
2594 break;
2596 case TOK_INC:
2597 case TOK_DEC:
2598 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2599 if (!pn)
2600 return NULL;
2601 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2602 if (!pn2)
2603 return NULL;
2604 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2605 return NULL;
2606 pn->pn_pos.end = pn2->pn_pos.end;
2607 break;
2609 case TOK_DELETE:
2610 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2611 if (!pn)
2612 return NULL;
2613 pn2 = UnaryExpr(cx, ts, tc);
2614 if (!pn2)
2615 return NULL;
2616 pn->pn_pos.end = pn2->pn_pos.end;
2619 * Under ECMA3, deleting any unary expression is valid -- it simply
2620 * returns true. Here we strip off any parentheses.
2622 while (pn2->pn_type == TOK_RP)
2623 pn2 = pn2->pn_kid;
2624 pn->pn_kid = pn2;
2625 break;
2627 case TOK_ERROR:
2628 return NULL;
2630 default:
2631 js_UngetToken(ts);
2632 pn = MemberExpr(cx, ts, tc, JS_TRUE);
2633 if (!pn)
2634 return NULL;
2636 /* Don't look across a newline boundary for a postfix incop. */
2637 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2638 tt = js_PeekTokenSameLine(cx, ts);
2639 if (tt == TOK_INC || tt == TOK_DEC) {
2640 (void) js_GetToken(cx, ts);
2641 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2642 if (!pn2)
2643 return NULL;
2644 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2645 return NULL;
2646 pn2->pn_pos.begin = pn->pn_pos.begin;
2647 pn = pn2;
2650 break;
2652 return pn;
2655 static JSBool
2656 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2657 JSParseNode *listNode)
2659 JSBool matched;
2661 ts->flags |= TSF_REGEXP;
2662 matched = js_MatchToken(cx, ts, TOK_RP);
2663 ts->flags &= ~TSF_REGEXP;
2664 if (!matched) {
2665 do {
2666 JSParseNode *argNode = AssignExpr(cx, ts, tc);
2667 if (!argNode)
2668 return JS_FALSE;
2669 PN_APPEND(listNode, argNode);
2670 } while (js_MatchToken(cx, ts, TOK_COMMA));
2672 if (js_GetToken(cx, ts) != TOK_RP) {
2673 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2674 JSMSG_PAREN_AFTER_ARGS);
2675 return JS_FALSE;
2678 return JS_TRUE;
2681 static JSParseNode *
2682 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2683 JSBool allowCallSyntax)
2685 JSParseNode *pn, *pn2, *pn3;
2686 JSTokenType tt;
2688 CHECK_RECURSION();
2690 /* Check for new expression first. */
2691 ts->flags |= TSF_REGEXP;
2692 tt = js_PeekToken(cx, ts);
2693 ts->flags &= ~TSF_REGEXP;
2694 if (tt == TOK_NEW) {
2695 (void) js_GetToken(cx, ts);
2697 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2698 if (!pn)
2699 return NULL;
2700 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2701 if (!pn2)
2702 return NULL;
2703 pn->pn_op = JSOP_NEW;
2704 PN_INIT_LIST_1(pn, pn2);
2705 pn->pn_pos.begin = pn2->pn_pos.begin;
2707 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2708 return NULL;
2709 if (pn->pn_count > ARGC_LIMIT) {
2710 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2711 JSMSG_TOO_MANY_CON_ARGS);
2712 return NULL;
2714 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2715 } else {
2716 pn = PrimaryExpr(cx, ts, tc);
2717 if (!pn)
2718 return NULL;
2721 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2722 if (tt == TOK_DOT) {
2723 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2724 if (!pn2)
2725 return NULL;
2726 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
2727 pn2->pn_pos.begin = pn->pn_pos.begin;
2728 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2729 pn2->pn_op = JSOP_GETPROP;
2730 pn2->pn_expr = pn;
2731 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2732 } else if (tt == TOK_LB) {
2733 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2734 if (!pn2)
2735 return NULL;
2736 pn3 = Expr(cx, ts, tc);
2737 if (!pn3)
2738 return NULL;
2740 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
2741 pn2->pn_pos.begin = pn->pn_pos.begin;
2742 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2744 /* Optimize o['p'] to o.p by rewriting pn2. */
2745 if (pn3->pn_type == TOK_STRING) {
2746 pn2->pn_type = TOK_DOT;
2747 pn2->pn_op = JSOP_GETPROP;
2748 pn2->pn_arity = PN_NAME;
2749 pn2->pn_expr = pn;
2750 pn2->pn_atom = pn3->pn_atom;
2751 } else {
2752 pn2->pn_op = JSOP_GETELEM;
2753 pn2->pn_left = pn;
2754 pn2->pn_right = pn3;
2756 } else if (allowCallSyntax && tt == TOK_LP) {
2757 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2758 if (!pn2)
2759 return NULL;
2761 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
2762 pn2->pn_op = JSOP_CALL;
2763 if (pn->pn_op == JSOP_NAME &&
2764 pn->pn_atom == cx->runtime->atomState.evalAtom) {
2765 pn2->pn_op = JSOP_EVAL;
2766 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2769 PN_INIT_LIST_1(pn2, pn);
2770 pn2->pn_pos.begin = pn->pn_pos.begin;
2772 if (!ArgumentList(cx, ts, tc, pn2))
2773 return NULL;
2774 if (pn2->pn_count > ARGC_LIMIT) {
2775 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2776 JSMSG_TOO_MANY_FUN_ARGS);
2777 return NULL;
2779 pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end;
2780 } else {
2781 js_UngetToken(ts);
2782 return pn;
2785 pn = pn2;
2787 if (tt == TOK_ERROR)
2788 return NULL;
2789 return pn;
2792 static JSParseNode *
2793 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2795 JSTokenType tt;
2796 JSParseNode *pn, *pn2, *pn3;
2797 char *badWord;
2798 #if JS_HAS_GETTER_SETTER
2799 JSAtom *atom;
2800 JSRuntime *rt;
2801 #endif
2803 #if JS_HAS_SHARP_VARS
2804 JSParseNode *defsharp;
2805 JSBool notsharp;
2807 defsharp = NULL;
2808 notsharp = JS_FALSE;
2809 again:
2811 * Control flows here after #n= is scanned. If the following primary is
2812 * not valid after such a "sharp variable" definition, the token type case
2813 * should set notsharp.
2815 #endif
2817 CHECK_RECURSION();
2819 ts->flags |= TSF_REGEXP;
2820 tt = js_GetToken(cx, ts);
2821 ts->flags &= ~TSF_REGEXP;
2823 #if JS_HAS_GETTER_SETTER
2824 if (tt == TOK_NAME) {
2825 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2826 if (tt == TOK_ERROR)
2827 return NULL;
2829 #endif
2831 switch (tt) {
2832 #if JS_HAS_LEXICAL_CLOSURE
2833 case TOK_FUNCTION:
2834 pn = FunctionExpr(cx, ts, tc);
2835 if (!pn)
2836 return NULL;
2837 break;
2838 #endif
2840 #if JS_HAS_INITIALIZERS
2841 case TOK_LB:
2843 JSBool matched;
2844 jsuint atomIndex;
2846 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2847 if (!pn)
2848 return NULL;
2849 pn->pn_type = TOK_RB;
2850 pn->pn_extra = 0;
2852 #if JS_HAS_SHARP_VARS
2853 if (defsharp) {
2854 PN_INIT_LIST_1(pn, defsharp);
2855 defsharp = NULL;
2856 } else
2857 #endif
2858 PN_INIT_LIST(pn);
2860 ts->flags |= TSF_REGEXP;
2861 matched = js_MatchToken(cx, ts, TOK_RB);
2862 ts->flags &= ~TSF_REGEXP;
2863 if (!matched) {
2864 for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {
2865 ts->flags |= TSF_REGEXP;
2866 tt = js_PeekToken(cx, ts);
2867 ts->flags &= ~TSF_REGEXP;
2868 if (tt == TOK_RB) {
2869 pn->pn_extra |= PNX_ENDCOMMA;
2870 break;
2873 if (tt == TOK_COMMA) {
2874 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
2875 js_MatchToken(cx, ts, TOK_COMMA);
2876 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2877 } else {
2878 pn2 = AssignExpr(cx, ts, tc);
2880 if (!pn2)
2881 return NULL;
2882 PN_APPEND(pn, pn2);
2884 if (tt != TOK_COMMA) {
2885 /* If we didn't already match TOK_COMMA in above case. */
2886 if (!js_MatchToken(cx, ts, TOK_COMMA))
2887 break;
2891 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
2893 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2894 return pn;
2897 case TOK_LC:
2898 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2899 if (!pn)
2900 return NULL;
2901 pn->pn_type = TOK_RC;
2903 #if JS_HAS_SHARP_VARS
2904 if (defsharp) {
2905 PN_INIT_LIST_1(pn, defsharp);
2906 defsharp = NULL;
2907 } else
2908 #endif
2909 PN_INIT_LIST(pn);
2911 if (!js_MatchToken(cx, ts, TOK_RC)) {
2912 do {
2913 JSOp op;
2915 tt = js_GetToken(cx, ts);
2916 switch (tt) {
2917 case TOK_NUMBER:
2918 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2919 if (pn3)
2920 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
2921 break;
2922 case TOK_NAME:
2923 #if JS_HAS_GETTER_SETTER
2924 atom = CURRENT_TOKEN(ts).t_atom;
2925 rt = cx->runtime;
2926 if (atom == rt->atomState.getAtom ||
2927 atom == rt->atomState.setAtom) {
2928 op = (atom == rt->atomState.getAtom)
2929 ? JSOP_GETTER
2930 : JSOP_SETTER;
2931 if (js_MatchToken(cx, ts, TOK_NAME)) {
2932 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME,
2933 tc);
2934 if (!pn3)
2935 return NULL;
2936 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2937 pn3->pn_expr = NULL;
2939 /* We have to fake a 'function' token here. */
2940 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
2941 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
2942 pn2 = FunctionExpr(cx, ts, tc);
2943 pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
2944 goto skip;
2947 /* else fall thru ... */
2948 #endif
2949 case TOK_STRING:
2950 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2951 if (pn3)
2952 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2953 break;
2954 case TOK_RC:
2955 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2956 JSREPORT_WARNING |
2957 JSREPORT_STRICT,
2958 JSMSG_TRAILING_COMMA)) {
2959 return NULL;
2961 goto end_obj_init;
2962 default:
2963 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2964 JSMSG_BAD_PROP_ID);
2965 return NULL;
2968 tt = js_GetToken(cx, ts);
2969 #if JS_HAS_GETTER_SETTER
2970 if (tt == TOK_NAME) {
2971 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
2972 if (tt == TOK_ERROR)
2973 return NULL;
2975 #endif
2976 if (tt != TOK_COLON) {
2977 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2978 JSMSG_COLON_AFTER_ID);
2979 return NULL;
2981 op = CURRENT_TOKEN(ts).t_op;
2982 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
2983 tc);
2984 #if JS_HAS_GETTER_SETTER
2985 skip:
2986 #endif
2987 if (!pn2)
2988 return NULL;
2989 PN_APPEND(pn, pn2);
2990 } while (js_MatchToken(cx, ts, TOK_COMMA));
2992 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
2994 end_obj_init:
2995 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2996 return pn;
2998 #if JS_HAS_SHARP_VARS
2999 case TOK_DEFSHARP:
3000 if (defsharp)
3001 goto badsharp;
3002 defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3003 if (!defsharp)
3004 return NULL;
3005 defsharp->pn_kid = NULL;
3006 defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
3007 goto again;
3009 case TOK_USESHARP:
3010 /* Check for forward/dangling references at runtime, to allow eval. */
3011 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3012 if (!pn)
3013 return NULL;
3014 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
3015 notsharp = JS_TRUE;
3016 break;
3017 #endif /* JS_HAS_SHARP_VARS */
3018 #endif /* JS_HAS_INITIALIZERS */
3020 case TOK_LP:
3022 #if JS_HAS_IN_OPERATOR
3023 uintN oldflags;
3024 #endif
3025 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3026 if (!pn)
3027 return NULL;
3028 #if JS_HAS_IN_OPERATOR
3030 * Always accept the 'in' operator in a parenthesized expression,
3031 * where it's unambiguous, even if we might be parsing the init of a
3032 * for statement.
3034 oldflags = tc->flags;
3035 tc->flags &= ~TCF_IN_FOR_INIT;
3036 #endif
3037 pn2 = Expr(cx, ts, tc);
3038 #if JS_HAS_IN_OPERATOR
3039 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3040 #endif
3041 if (!pn2)
3042 return NULL;
3044 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
3045 pn->pn_type = TOK_RP;
3046 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3047 pn->pn_kid = pn2;
3048 break;
3051 case TOK_STRING:
3052 #if JS_HAS_SHARP_VARS
3053 notsharp = JS_TRUE;
3054 #endif
3055 /* FALL THROUGH */
3056 case TOK_NAME:
3057 case TOK_OBJECT:
3058 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3059 if (!pn)
3060 return NULL;
3061 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3062 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3063 if (tt == TOK_NAME) {
3064 pn->pn_arity = PN_NAME;
3065 pn->pn_expr = NULL;
3066 pn->pn_slot = -1;
3067 pn->pn_attrs = 0;
3069 /* Unqualified __parent__ and __proto__ uses require activations. */
3070 if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
3071 pn->pn_atom == cx->runtime->atomState.protoAtom) {
3072 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3075 break;
3077 case TOK_NUMBER:
3078 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3079 if (!pn)
3080 return NULL;
3081 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
3082 #if JS_HAS_SHARP_VARS
3083 notsharp = JS_TRUE;
3084 #endif
3085 break;
3087 case TOK_PRIMARY:
3088 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3089 if (!pn)
3090 return NULL;
3091 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3092 #if JS_HAS_SHARP_VARS
3093 notsharp = JS_TRUE;
3094 #endif
3095 break;
3097 #if !JS_HAS_EXPORT_IMPORT
3098 case TOK_EXPORT:
3099 case TOK_IMPORT:
3100 #endif
3101 case TOK_RESERVED:
3102 badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr,
3103 (size_t) CURRENT_TOKEN(ts).pos.end.index
3104 - CURRENT_TOKEN(ts).pos.begin.index);
3105 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3106 JSMSG_RESERVED_ID, badWord);
3107 JS_free(cx, badWord);
3108 return NULL;
3110 case TOK_ERROR:
3111 /* The scanner or one of its subroutines reported the error. */
3112 return NULL;
3114 default:
3115 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3116 JSMSG_SYNTAX_ERROR);
3117 return NULL;
3120 #if JS_HAS_SHARP_VARS
3121 if (defsharp) {
3122 if (notsharp) {
3123 badsharp:
3124 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3125 JSMSG_BAD_SHARP_VAR_DEF);
3126 return NULL;
3128 defsharp->pn_kid = pn;
3129 return defsharp;
3131 #endif
3132 return pn;
3135 static JSBool
3136 ContainsVarStmt(JSParseNode *pn)
3138 JSParseNode *pn2;
3140 if (!pn)
3141 return JS_FALSE;
3142 switch (pn->pn_arity) {
3143 case PN_LIST:
3144 if (pn->pn_type == TOK_VAR)
3145 return JS_TRUE;
3146 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3147 if (ContainsVarStmt(pn2))
3148 return JS_TRUE;
3150 break;
3151 case PN_TERNARY:
3152 return ContainsVarStmt(pn->pn_kid1) ||
3153 ContainsVarStmt(pn->pn_kid2) ||
3154 ContainsVarStmt(pn->pn_kid3);
3155 case PN_BINARY:
3157 * Limit recursion if pn is a binary expression, which can't contain a
3158 * var statement.
3160 if (pn->pn_op != JSOP_NOP)
3161 return JS_FALSE;
3162 return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
3163 case PN_UNARY:
3164 if (pn->pn_op != JSOP_NOP)
3165 return JS_FALSE;
3166 return ContainsVarStmt(pn->pn_kid);
3167 default:;
3169 return JS_FALSE;
3173 * Fold from one constant type to another.
3174 * XXX handles only strings and numbers for now
3176 static JSBool
3177 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
3179 if (pn->pn_type != type) {
3180 switch (type) {
3181 case TOK_NUMBER:
3182 if (pn->pn_type == TOK_STRING) {
3183 jsdouble d;
3184 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
3185 return JS_FALSE;
3186 pn->pn_dval = d;
3187 pn->pn_type = TOK_NUMBER;
3188 pn->pn_op = JSOP_NUMBER;
3190 break;
3192 case TOK_STRING:
3193 if (pn->pn_type == TOK_NUMBER) {
3194 JSString *str = js_NumberToString(cx, pn->pn_dval);
3195 if (!str)
3196 return JS_FALSE;
3197 pn->pn_atom = js_AtomizeString(cx, str, 0);
3198 if (!pn->pn_atom)
3199 return JS_FALSE;
3200 pn->pn_type = TOK_STRING;
3201 pn->pn_op = JSOP_STRING;
3203 break;
3205 default:;
3208 return JS_TRUE;
3212 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
3213 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
3214 * a successful call to this function.
3216 static JSBool
3217 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
3218 JSParseNode *pn, JSTreeContext *tc)
3220 jsdouble d, d2;
3221 int32 i, j;
3222 uint32 u;
3224 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
3225 d = pn1->pn_dval;
3226 d2 = pn2->pn_dval;
3227 switch (op) {
3228 case JSOP_LSH:
3229 case JSOP_RSH:
3230 if (!js_DoubleToECMAInt32(cx, d, &i))
3231 return JS_FALSE;
3232 if (!js_DoubleToECMAInt32(cx, d2, &j))
3233 return JS_FALSE;
3234 j &= 31;
3235 d = (op == JSOP_LSH) ? i << j : i >> j;
3236 break;
3238 case JSOP_URSH:
3239 if (!js_DoubleToECMAUint32(cx, d, &u))
3240 return JS_FALSE;
3241 if (!js_DoubleToECMAInt32(cx, d2, &j))
3242 return JS_FALSE;
3243 j &= 31;
3244 d = u >> j;
3245 break;
3247 case JSOP_ADD:
3248 d += d2;
3249 break;
3251 case JSOP_SUB:
3252 d -= d2;
3253 break;
3255 case JSOP_MUL:
3256 d *= d2;
3257 break;
3259 case JSOP_DIV:
3260 if (d2 == 0) {
3261 #if defined(XP_WIN)
3262 /* XXX MSVC miscompiles such that (NaN == 0) */
3263 if (JSDOUBLE_IS_NaN(d2))
3264 d = *cx->runtime->jsNaN;
3265 else
3266 #endif
3267 if (d == 0 || JSDOUBLE_IS_NaN(d))
3268 d = *cx->runtime->jsNaN;
3269 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3270 d = *cx->runtime->jsNegativeInfinity;
3271 else
3272 d = *cx->runtime->jsPositiveInfinity;
3273 } else {
3274 d /= d2;
3276 break;
3278 case JSOP_MOD:
3279 if (d2 == 0) {
3280 d = *cx->runtime->jsNaN;
3281 } else {
3282 #if defined(XP_WIN)
3283 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3284 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
3285 #endif
3286 d = fmod(d, d2);
3288 break;
3290 default:;
3293 /* Take care to allow pn1 or pn2 to alias pn. */
3294 if (pn1 != pn)
3295 RecycleTree(pn1, tc);
3296 if (pn2 != pn)
3297 RecycleTree(pn2, tc);
3298 pn->pn_type = TOK_NUMBER;
3299 pn->pn_op = JSOP_NUMBER;
3300 pn->pn_arity = PN_NULLARY;
3301 pn->pn_dval = d;
3302 return JS_TRUE;
3305 JSBool
3306 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3308 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
3309 int stackDummy;
3311 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
3312 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
3313 return JS_FALSE;
3316 switch (pn->pn_arity) {
3317 case PN_FUNC:
3318 if (!js_FoldConstants(cx, pn->pn_body, tc))
3319 return JS_FALSE;
3320 break;
3322 case PN_LIST:
3323 /* Save the list head in pn1 for later use. */
3324 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3325 if (!js_FoldConstants(cx, pn2, tc))
3326 return JS_FALSE;
3328 break;
3330 case PN_TERNARY:
3331 /* Any kid may be null (e.g. for (;;)). */
3332 pn1 = pn->pn_kid1;
3333 pn2 = pn->pn_kid2;
3334 pn3 = pn->pn_kid3;
3335 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3336 return JS_FALSE;
3337 if (pn2 && !js_FoldConstants(cx, pn2, tc))
3338 return JS_FALSE;
3339 if (pn3 && !js_FoldConstants(cx, pn3, tc))
3340 return JS_FALSE;
3341 break;
3343 case PN_BINARY:
3344 /* First kid may be null (for default case in switch). */
3345 pn1 = pn->pn_left;
3346 pn2 = pn->pn_right;
3347 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3348 return JS_FALSE;
3349 if (!js_FoldConstants(cx, pn2, tc))
3350 return JS_FALSE;
3351 break;
3353 case PN_UNARY:
3354 /* Our kid may be null (e.g. return; vs. return e;). */
3355 pn1 = pn->pn_kid;
3356 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3357 return JS_FALSE;
3358 break;
3360 case PN_NAME:
3362 * Skip pn1 down along a chain of dotted member expressions to avoid
3363 * excessive recursion. Our only goal here is to fold constants (if
3364 * any) in the primary expression operand to the left of the first
3365 * dot in the chain.
3367 pn1 = pn->pn_expr;
3368 while (pn1 && pn1->pn_arity == PN_NAME)
3369 pn1 = pn1->pn_expr;
3370 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3371 return JS_FALSE;
3372 break;
3374 case PN_NULLARY:
3375 break;
3378 switch (pn->pn_type) {
3379 case TOK_IF:
3380 if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
3381 break;
3382 /* FALL THROUGH */
3384 case TOK_HOOK:
3385 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
3386 switch (pn1->pn_type) {
3387 case TOK_NUMBER:
3388 if (pn1->pn_dval == 0)
3389 pn2 = pn3;
3390 break;
3391 case TOK_STRING:
3392 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
3393 pn2 = pn3;
3394 break;
3395 case TOK_PRIMARY:
3396 if (pn1->pn_op == JSOP_TRUE)
3397 break;
3398 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
3399 pn2 = pn3;
3400 break;
3402 /* FALL THROUGH */
3403 default:
3404 /* Early return to dodge common code that copies pn2 to pn. */
3405 return JS_TRUE;
3408 if (pn2) {
3409 /* pn2 is the then- or else-statement subtree to compile. */
3410 PN_MOVE_NODE(pn, pn2);
3411 } else {
3412 /* False condition and no else: make pn an empty statement. */
3413 pn->pn_type = TOK_SEMI;
3414 pn->pn_arity = PN_UNARY;
3415 pn->pn_kid = NULL;
3417 RecycleTree(pn2, tc);
3418 if (pn3 && pn3 != pn2)
3419 RecycleTree(pn3, tc);
3420 break;
3422 case TOK_PLUS:
3423 if (pn->pn_arity == PN_LIST) {
3424 size_t length, length2;
3425 jschar *chars;
3426 JSString *str, *str2;
3429 * Any string literal term with all others number or string means
3430 * this is a concatenation. If any term is not a string or number
3431 * literal, we can't fold.
3433 JS_ASSERT(pn->pn_count > 2);
3434 if (pn->pn_extra & PNX_CANTFOLD)
3435 return JS_TRUE;
3436 if (pn->pn_extra != PNX_STRCAT)
3437 goto do_binary_op;
3439 /* Ok, we're concatenating: convert non-string constant operands. */
3440 length = 0;
3441 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3442 if (!FoldType(cx, pn2, TOK_STRING))
3443 return JS_FALSE;
3444 /* XXX fold only if all operands convert to string */
3445 if (pn2->pn_type != TOK_STRING)
3446 return JS_TRUE;
3447 length += ATOM_TO_STRING(pn2->pn_atom)->length;
3450 /* Allocate a new buffer and string descriptor for the result. */
3451 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
3452 if (!chars)
3453 return JS_FALSE;
3454 str = js_NewString(cx, chars, length, 0);
3455 if (!str) {
3456 JS_free(cx, chars);
3457 return JS_FALSE;
3460 /* Fill the buffer, advancing chars and recycling kids as we go. */
3461 for (pn2 = pn1; pn2; pn2 = pn3) {
3462 str2 = ATOM_TO_STRING(pn2->pn_atom);
3463 length2 = str2->length;
3464 js_strncpy(chars, str2->chars, length2);
3465 chars += length2;
3466 pn3 = pn2->pn_next;
3467 RecycleTree(pn2, tc);
3469 *chars = 0;
3471 /* Atomize the result string and mutate pn to refer to it. */
3472 pn->pn_atom = js_AtomizeString(cx, str, 0);
3473 if (!pn->pn_atom)
3474 return JS_FALSE;
3475 pn->pn_type = TOK_STRING;
3476 pn->pn_op = JSOP_STRING;
3477 pn->pn_arity = PN_NULLARY;
3478 break;
3481 /* Handle a binary string concatenation. */
3482 JS_ASSERT(pn->pn_arity == PN_BINARY);
3483 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
3484 JSString *left, *right, *str;
3486 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
3487 TOK_STRING)) {
3488 return JS_FALSE;
3490 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
3491 return JS_TRUE;
3492 left = ATOM_TO_STRING(pn1->pn_atom);
3493 right = ATOM_TO_STRING(pn2->pn_atom);
3494 str = js_ConcatStrings(cx, left, right);
3495 if (!str)
3496 return JS_FALSE;
3497 pn->pn_atom = js_AtomizeString(cx, str, 0);
3498 if (!pn->pn_atom)
3499 return JS_FALSE;
3500 pn->pn_type = TOK_STRING;
3501 pn->pn_op = JSOP_STRING;
3502 pn->pn_arity = PN_NULLARY;
3503 RecycleTree(pn1, tc);
3504 RecycleTree(pn2, tc);
3505 break;
3508 /* Can't concatenate string literals, let's try numbers. */
3509 goto do_binary_op;
3511 case TOK_STAR:
3512 /* The * in 'import *;' parses as a nullary star node. */
3513 if (pn->pn_arity == PN_NULLARY)
3514 break;
3515 /* FALL THROUGH */
3517 case TOK_SHOP:
3518 case TOK_MINUS:
3519 case TOK_DIVOP:
3520 do_binary_op:
3521 if (pn->pn_arity == PN_LIST) {
3522 JS_ASSERT(pn->pn_count > 2);
3523 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3524 if (!FoldType(cx, pn2, TOK_NUMBER))
3525 return JS_FALSE;
3526 /* XXX fold only if all operands convert to number */
3527 if (pn2->pn_type != TOK_NUMBER)
3528 break;
3530 if (!pn2) {
3531 JSOp op = pn->pn_op;
3533 pn2 = pn1->pn_next;
3534 pn3 = pn2->pn_next;
3535 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
3536 return JS_FALSE;
3537 while ((pn2 = pn3) != NULL) {
3538 pn3 = pn2->pn_next;
3539 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
3540 return JS_FALSE;
3543 } else {
3544 JS_ASSERT(pn->pn_arity == PN_BINARY);
3545 if (!FoldType(cx, pn1, TOK_NUMBER) ||
3546 !FoldType(cx, pn2, TOK_NUMBER)) {
3547 return JS_FALSE;
3549 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
3550 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
3551 return JS_FALSE;
3554 break;
3556 case TOK_UNARYOP:
3557 if (pn1->pn_type == TOK_NUMBER) {
3558 jsdouble d;
3559 int32 i;
3561 /* Operate on one numeric constant. */
3562 d = pn1->pn_dval;
3563 switch (pn->pn_op) {
3564 case JSOP_BITNOT:
3565 if (!js_DoubleToECMAInt32(cx, d, &i))
3566 return JS_FALSE;
3567 d = ~i;
3568 break;
3570 case JSOP_NEG:
3571 #ifdef HPUX
3573 * Negation of a zero doesn't produce a negative
3574 * zero on HPUX. Perform the operation by bit
3575 * twiddling.
3577 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
3578 #else
3579 d = -d;
3580 #endif
3581 break;
3583 case JSOP_POS:
3584 break;
3586 case JSOP_NOT:
3587 pn->pn_type = TOK_PRIMARY;
3588 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
3589 pn->pn_arity = PN_NULLARY;
3590 /* FALL THROUGH */
3592 default:
3593 /* Return early to dodge the common TOK_NUMBER code. */
3594 return JS_TRUE;
3596 pn->pn_type = TOK_NUMBER;
3597 pn->pn_op = JSOP_NUMBER;
3598 pn->pn_arity = PN_NULLARY;
3599 pn->pn_dval = d;
3600 RecycleTree(pn1, tc);
3602 break;
3604 default:;
3607 return JS_TRUE;