1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
40 /*
41 * JS parser.
42 *
43 * This is a recursive-descent parser for the JavaScript language specified by
44 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
45 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
46 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
47 * After tree construction, it rewrites trees to fold constants and evaluate
48 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
49 * generate bytecode.
50 *
51 * This parser attempts no error recovery. The dense JSTokenType enumeration
52 * was designed with error recovery built on 64-bit first and follow bitsets
53 * in mind, however.
54 */
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"
79 /*
80 * JS parsers, from lowest to highest precedence.
81 *
82 * Each parser takes a context and a token stream, and emits bytecode using
83 * a code generator.
84 */
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;
117 /*
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.
120 */
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; \
126 } \
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; \
136 } \
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)
147 {
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
156 }
158 static JSParseNode *
159 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
160 {
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
184 }
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;
203 }
204 }
205 return pn;
206 }
208 /*
209 * Allocate a JSParseNode from cx's temporary arena.
210 */
211 static JSParseNode *
212 NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity,
213 JSTreeContext *tc)
214 {
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;
231 }
233 static JSParseNode *
234 NewBinary(JSContext *cx, JSTokenType tt,
235 JSOp op, JSParseNode *left, JSParseNode *right,
236 JSTreeContext *tc)
237 {
238 JSParseNode *pn, *pn1, *pn2;
240 if (!left || !right)
241 return NULL;
243 /*
244 * Flatten a left-associative (left-heavy) tree of a given operator into
245 * a list, to reduce js_FoldConstants and js_EmitTree recursion.
246 */
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;
265 }
266 }
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;
274 }
275 return left;
276 }
278 /*
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").
284 */
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 left->pn_pos.end = right->pn_pos.end;
290 RecycleTree(right, tc);
291 return left;
292 }
294 pn = NewOrRecycledNode(cx, tc);
295 if (!pn)
296 return NULL;
297 pn->pn_type = tt;
298 pn->pn_pos.begin = left->pn_pos.begin;
299 pn->pn_pos.end = right->pn_pos.end;
300 pn->pn_op = op;
301 pn->pn_arity = PN_BINARY;
302 pn->pn_left = left;
303 pn->pn_right = right;
304 pn->pn_next = NULL;
305 #ifdef METER_PARSENODES
306 parsenodes++;
307 if (parsenodes - recyclednodes > maxparsenodes)
308 maxparsenodes = parsenodes - recyclednodes;
309 #endif
310 return pn;
311 }
313 #if JS_HAS_GETTER_SETTER
314 static JSTokenType
315 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
316 {
317 JSAtom *atom;
318 JSRuntime *rt;
319 JSOp op;
320 const char *name;
322 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
323 atom = CURRENT_TOKEN(ts).t_atom;
324 rt = cx->runtime;
325 if (atom == rt->atomState.getterAtom)
326 op = JSOP_GETTER;
327 else if (atom == rt->atomState.setterAtom)
328 op = JSOP_SETTER;
329 else
330 return TOK_NAME;
331 if (js_PeekTokenSameLine(cx, ts) != tt)
332 return TOK_NAME;
333 (void) js_GetToken(cx, ts);
334 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
335 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
336 JSMSG_BAD_GETTER_OR_SETTER,
337 (op == JSOP_GETTER)
338 ? js_getter_str
339 : js_setter_str);
340 return TOK_ERROR;
341 }
342 CURRENT_TOKEN(ts).t_op = op;
343 name = js_AtomToPrintableString(cx, atom);
344 if (!name ||
345 !js_ReportCompileErrorNumber(cx, ts, NULL,
346 JSREPORT_WARNING |
347 JSREPORT_STRICT,
348 JSMSG_DEPRECATED_USAGE,
349 name)) {
350 return TOK_ERROR;
351 }
352 return tt;
353 }
354 #endif
356 /*
357 * Parse a top-level JS script.
358 */
359 JS_FRIEND_API(JSParseNode *)
360 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
361 {
362 JSStackFrame *fp, frame;
363 JSTreeContext tc;
364 JSParseNode *pn;
366 /*
367 * Push a compiler frame if we have no frames, or if the top frame is a
368 * lightweight function activation, or if its scope chain doesn't match
369 * the one passed to us.
370 */
371 fp = cx->fp;
372 if (!fp || !fp->varobj || fp->scopeChain != chain) {
373 memset(&frame, 0, sizeof frame);
374 frame.varobj = frame.scopeChain = chain;
375 if (cx->options & JSOPTION_VAROBJFIX) {
376 while ((chain = JS_GetParent(cx, chain)) != NULL)
377 frame.varobj = chain;
378 }
379 frame.down = fp;
380 cx->fp = &frame;
381 }
383 /*
384 * Protect atoms from being collected by a GC activation, which might
385 * - nest on this thread due to out of memory (the so-called "last ditch"
386 * GC attempted within js_AllocGCThing), or
387 * - run for any reason on another thread if this thread is suspended on
388 * an object lock before it finishes generating bytecode into a script
389 * protected from the GC by a root or a stack frame reference.
390 */
391 JS_KEEP_ATOMS(cx->runtime);
392 TREE_CONTEXT_INIT(&tc);
393 pn = Statements(cx, ts, &tc);
394 if (pn) {
395 if (!js_MatchToken(cx, ts, TOK_EOF)) {
396 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
397 JSMSG_SYNTAX_ERROR);
398 pn = NULL;
399 } else {
400 pn->pn_type = TOK_LC;
401 if (!js_FoldConstants(cx, pn, &tc))
402 pn = NULL;
403 }
404 }
406 TREE_CONTEXT_FINISH(&tc);
407 JS_UNKEEP_ATOMS(cx->runtime);
408 cx->fp = fp;
409 return pn;
410 }
412 /*
413 * Compile a top-level script.
414 */
415 JS_FRIEND_API(JSBool)
416 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
417 JSCodeGenerator *cg)
418 {
419 JSStackFrame *fp, frame;
420 uint32 flags;
421 JSParseNode *pn;
422 JSBool ok;
423 #ifdef METER_PARSENODES
424 void *sbrk(ptrdiff_t), *before = sbrk(0);
425 #endif
427 /*
428 * Push a compiler frame if we have no frames, or if the top frame is a
429 * lightweight function activation, or if its scope chain doesn't match
430 * the one passed to us.
431 */
432 fp = cx->fp;
433 if (!fp || !fp->varobj || fp->scopeChain != chain) {
434 memset(&frame, 0, sizeof frame);
435 frame.varobj = frame.scopeChain = chain;
436 if (cx->options & JSOPTION_VAROBJFIX) {
437 while ((chain = JS_GetParent(cx, chain)) != NULL)
438 frame.varobj = chain;
439 }
440 frame.down = fp;
441 cx->fp = &frame;
442 }
443 flags = cx->fp->flags;
444 cx->fp->flags = flags |
445 (JS_HAS_COMPILE_N_GO_OPTION(cx)
446 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
447 : JSFRAME_COMPILING);
449 /* Prevent GC activation while compiling. */
450 JS_KEEP_ATOMS(cx->runtime);
452 pn = Statements(cx, ts, &cg->treeContext);
453 if (!pn) {
454 ok = JS_FALSE;
455 } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
456 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
457 JSMSG_SYNTAX_ERROR);
458 ok = JS_FALSE;
459 } else {
460 #ifdef METER_PARSENODES
461 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
462 (char *)sbrk(0) - (char *)before,
463 parsenodes,
464 maxparsenodes,
465 parsenodes - recyclednodes);
466 before = sbrk(0);
467 #endif
469 /*
470 * No need to emit code here -- Statements already has, for each
471 * statement in turn. Search for TCF_COMPILING in Statements, below.
472 * That flag is set for every tc == &cg->treeContext, and it implies
473 * that the tc can be downcast to a cg and used to emit code during
474 * parsing, rather than at the end of the parse phase.
475 */
476 JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
477 ok = JS_TRUE;
478 }
480 #ifdef METER_PARSENODES
481 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
482 (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
483 #endif
484 #ifdef JS_ARENAMETER
485 JS_DumpArenaStats(stdout);
486 #endif
487 JS_UNKEEP_ATOMS(cx->runtime);
488 cx->fp->flags = flags;
489 cx->fp = fp;
490 return ok;
491 }
493 /*
494 * Insist on a final return before control flows out of pn, but don't be too
495 * smart about loops (do {...; return e2;} while(0) at the end of a function
496 * that contains an early return e1 will get a strict-option-only warning).
497 */
498 #define ENDS_IN_OTHER 0
499 #define ENDS_IN_RETURN 1
500 #define ENDS_IN_BREAK 2
502 static int
503 HasFinalReturn(JSParseNode *pn)
504 {
505 uintN rv, rv2, hasDefault;
506 JSParseNode *pn2, *pn3;
508 switch (pn->pn_type) {
509 case TOK_LC:
510 if (!pn->pn_head)
511 return ENDS_IN_OTHER;
512 return HasFinalReturn(PN_LAST(pn));
514 case TOK_IF:
515 rv = HasFinalReturn(pn->pn_kid2);
516 if (pn->pn_kid3)
517 rv &= HasFinalReturn(pn->pn_kid3);
518 return rv;
520 #if JS_HAS_SWITCH_STATEMENT
521 case TOK_SWITCH:
522 rv = ENDS_IN_RETURN;
523 hasDefault = ENDS_IN_OTHER;
524 for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
525 if (pn2->pn_type == TOK_DEFAULT)
526 hasDefault = ENDS_IN_RETURN;
527 pn3 = pn2->pn_right;
528 JS_ASSERT(pn3->pn_type == TOK_LC);
529 if (pn3->pn_head) {
530 rv2 = HasFinalReturn(PN_LAST(pn3));
531 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
532 /* Falling through to next case or default. */;
533 else
534 rv &= rv2;
535 }
536 }
537 /* If a final switch has no default case, we judge it harshly. */
538 rv &= hasDefault;
539 return rv;
540 #endif /* JS_HAS_SWITCH_STATEMENT */
542 case TOK_BREAK:
543 return ENDS_IN_BREAK;
545 case TOK_WITH:
546 return HasFinalReturn(pn->pn_right);
548 case TOK_RETURN:
549 return ENDS_IN_RETURN;
551 #if JS_HAS_EXCEPTIONS
552 case TOK_THROW:
553 return ENDS_IN_RETURN;
555 case TOK_TRY:
556 /* If we have a finally block that returns, we are done. */
557 if (pn->pn_kid3) {
558 rv = HasFinalReturn(pn->pn_kid3);
559 if (rv == ENDS_IN_RETURN)
560 return rv;
561 }
563 /* Else check the try block and any and all catch statements. */
564 rv = HasFinalReturn(pn->pn_kid1);
565 if (pn->pn_kid2)
566 rv &= HasFinalReturn(pn->pn_kid2);
567 return rv;
569 case TOK_CATCH:
570 /* Check this block's code and iterate over further catch blocks. */
571 rv = HasFinalReturn(pn->pn_kid3);
572 for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
573 rv &= HasFinalReturn(pn2->pn_kid3);
574 return rv;
575 #endif
577 default:
578 return ENDS_IN_OTHER;
579 }
580 }
582 static JSBool
583 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
584 {
585 JSFunction *fun;
586 JSBool ok;
588 fun = cx->fp->fun;
589 if (fun->atom) {
590 char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
591 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
592 JSREPORT_WARNING |
593 JSREPORT_STRICT,
594 JSMSG_NO_RETURN_VALUE, name);
595 } else {
596 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
597 JSREPORT_WARNING |
598 JSREPORT_STRICT,
599 JSMSG_ANON_NO_RETURN_VALUE);
600 }
601 return ok;
602 }
604 static JSBool
605 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
606 {
607 return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
608 }
610 static JSParseNode *
611 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
612 JSTreeContext *tc)
613 {
614 JSStackFrame *fp, frame;
615 JSObject *funobj;
616 uintN oldflags;
617 JSParseNode *pn;
619 fp = cx->fp;
620 funobj = fun->object;
621 if (!fp || fp->fun != fun || fp->varobj != funobj ||
622 fp->scopeChain != funobj) {
623 memset(&frame, 0, sizeof frame);
624 frame.fun = fun;
625 frame.varobj = frame.scopeChain = funobj;
626 frame.down = fp;
627 frame.flags = (fp->flags & JSFRAME_COMPILE_N_GO);
628 cx->fp = &frame;
629 }
631 oldflags = tc->flags;
632 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
633 tc->flags |= TCF_IN_FUNCTION;
634 pn = Statements(cx, ts, tc);
636 /* Check for falling off the end of a function that returns a value. */
637 if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
638 if (!CheckFinalReturn(cx, ts, pn))
639 pn = NULL;
640 }
642 cx->fp = fp;
643 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
644 return pn;
645 }
647 /*
648 * Compile a JS function body, which might appear as the value of an event
649 * handler attribute in an HTML <INPUT> tag.
650 */
651 JSBool
652 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
653 {
654 JSArenaPool codePool, notePool;
655 JSCodeGenerator funcg;
656 JSStackFrame *fp, frame;
657 JSObject *funobj;
658 JSParseNode *pn;
659 JSBool ok;
661 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
662 JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote));
663 if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool,
664 ts->filename, ts->lineno,
665 ts->principals)) {
666 return JS_FALSE;
667 }
669 /* Prevent GC activation while compiling. */
670 JS_KEEP_ATOMS(cx->runtime);
672 /* Push a JSStackFrame for use by FunctionBody. */
673 fp = cx->fp;
674 funobj = fun->object;
675 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
676 fp->scopeChain != funobj));
677 memset(&frame, 0, sizeof frame);
678 frame.fun = fun;
679 frame.varobj = frame.scopeChain = funobj;
680 frame.down = fp;
681 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
682 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
683 : JSFRAME_COMPILING;
684 cx->fp = &frame;
686 /* Ensure that the body looks like a block statement to js_EmitTree. */
687 CURRENT_TOKEN(ts).type = TOK_LC;
688 pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
689 if (!pn) {
690 ok = JS_FALSE;
691 } else {
692 /*
693 * No need to emit code here -- Statements (via FunctionBody) already
694 * has. See similar comment in js_CompileTokenStream, and bug 108257.
695 */
696 fun->u.script = js_NewScriptFromCG(cx, &funcg, fun);
697 if (!fun->u.script) {
698 ok = JS_FALSE;
699 } else {
700 fun->interpreted = JS_TRUE;
701 if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
702 fun->flags |= JSFUN_HEAVYWEIGHT;
703 ok = JS_TRUE;
704 }
705 }
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(¬ePool);
713 return ok;
714 }
716 static JSParseNode *
717 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
718 JSBool lambda)
719 {
720 JSOp op, prevop;
721 JSParseNode *pn, *body;
722 JSAtom *funAtom, *argAtom;
723 JSStackFrame *fp;
724 JSObject *varobj, *pobj;
725 JSAtomListElement *ale;
726 JSProperty *prop;
727 JSFunction *fun;
728 uintN dupflag;
729 JSBool ok;
730 JSTreeContext funtc;
732 /* Make a TOK_FUNCTION node. */
733 #if JS_HAS_GETTER_SETTER
734 op = CURRENT_TOKEN(ts).t_op;
735 #endif
736 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc);
737 if (!pn)
738 return NULL;
740 /* Scan the optional function name into funAtom. */
741 funAtom = js_MatchToken(cx, ts, TOK_NAME) ? CURRENT_TOKEN(ts).t_atom : NULL;
743 /* Find the nearest variable-declaring scope and use it as our parent. */
744 fp = cx->fp;
745 varobj = fp->varobj;
747 /*
748 * Record names for function statements in tc->decls so we know when to
749 * avoid optimizing variable references that might name a function.
750 */
751 if (!lambda && funAtom) {
752 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
753 if (ale) {
754 prevop = ALE_JSOP(ale);
755 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
756 const char *name = js_AtomToPrintableString(cx, funAtom);
757 if (!name ||
758 !js_ReportCompileErrorNumber(cx, ts, NULL,
759 (prevop != JSOP_DEFCONST)
760 ? JSREPORT_WARNING |
761 JSREPORT_STRICT
762 : JSREPORT_ERROR,
763 JSMSG_REDECLARED_VAR,
764 (prevop == JSOP_DEFFUN ||
765 prevop == JSOP_CLOSURE)
766 ? js_function_str
767 : (prevop == JSOP_DEFCONST)
768 ? js_const_str
769 : js_var_str,
770 name)) {
771 return NULL;
772 }
773 }
774 if (tc->topStmt && prevop == JSOP_DEFVAR)
775 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
776 } else {
777 ale = js_IndexAtom(cx, funAtom, &tc->decls);
778 if (!ale)
779 return NULL;
780 }
781 ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
783 #if JS_HAS_LEXICAL_CLOSURE
784 /*
785 * A function nested at top level inside another's body needs only a
786 * local variable to bind its name to its value, and not an activation
787 * object property (it might also need the activation property, if the
788 * outer function contains with statements, e.g., but the stack slot
789 * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
790 * JSOP_GETVAR bytecode).
791 */
792 if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
793 /*
794 * Define a property on the outer function so that LookupArgOrVar
795 * can properly optimize accesses.
796 *
797 * XXX Here and in Variables, we use the function object's scope,
798 * XXX arguably polluting it, when we could use a compiler-private
799 * XXX scope structure. Tradition!
800 */
801 JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
802 JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
803 if (!js_LookupProperty(cx, varobj, (jsid)funAtom, &pobj, &prop))
804 return NULL;
805 if (prop)
806 OBJ_DROP_PROPERTY(cx, pobj, prop);
807 if (!prop || pobj != varobj) {
808 if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom,
809 JSVAL_VOID,
810 js_GetLocalVariable,
811 js_SetLocalVariable,
812 JSPROP_ENUMERATE | JSPROP_SHARED,
813 SPROP_HAS_SHORTID, fp->fun->nvars,
814 NULL)) {
815 return NULL;
816 }
817 fp->fun->nvars++;
818 }
819 }
820 #endif
821 }
823 fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
824 funAtom);
825 if (!fun)
826 return NULL;
827 #if JS_HAS_GETTER_SETTER
828 if (op != JSOP_NOP)
829 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
830 #endif
832 /* Now parse formal argument list and compute fun->nargs. */
833 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
834 if (!js_MatchToken(cx, ts, TOK_RP)) {
835 do {
836 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
837 argAtom = CURRENT_TOKEN(ts).t_atom;
838 pobj = NULL;
839 if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,
840 &prop)) {
841 return NULL;
842 }
843 dupflag = 0;
844 if (prop) {
845 ok = JS_TRUE;
846 if (pobj == fun->object &&
847 ((JSScopeProperty *) prop)->getter == js_GetArgument) {
848 const char *name = js_AtomToPrintableString(cx, argAtom);
850 /*
851 * A duplicate parameter name. We force a duplicate node
852 * on the SCOPE_LAST_PROP(scope) list with the same id,
853 * distinguished by the SPROP_IS_DUPLICATE flag, and not
854 * mapped by an entry in scope.
855 */
856 ok = name &&
857 js_ReportCompileErrorNumber(cx, ts, NULL,
858 JSREPORT_WARNING |
859 JSREPORT_STRICT,
860 JSMSG_DUPLICATE_FORMAL,
861 name);
863 dupflag = SPROP_IS_DUPLICATE;
864 }
865 OBJ_DROP_PROPERTY(cx, pobj, prop);
866 if (!ok)
867 return NULL;
868 prop = NULL;
869 }
870 if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom,
871 js_GetArgument, js_SetArgument,
872 SPROP_INVALID_SLOT,
873 JSPROP_ENUMERATE | JSPROP_PERMANENT |
874 JSPROP_SHARED,
875 SPROP_HAS_SHORTID | dupflag,
876 fun->nargs)) {
877 return NULL;
878 }
879 fun->nargs++;
880 } while (js_MatchToken(cx, ts, TOK_COMMA));
882 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
883 }
885 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
886 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
888 TREE_CONTEXT_INIT(&funtc);
889 body = FunctionBody(cx, ts, fun, &funtc);
890 if (!body)
891 return NULL;
893 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
894 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
896 #if JS_HAS_LEXICAL_CLOSURE
897 /*
898 * If we collected flags that indicate nested heavyweight functions, or
899 * this function contains heavyweight-making statements (references to
900 * __parent__ or __proto__; use of with, eval, import, or export; and
901 * assignment to arguments), flag the function as heavyweight (requiring
902 * a call object per invocation).
903 */
904 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
905 fun->flags |= JSFUN_HEAVYWEIGHT;
906 tc->flags |= TCF_FUN_HEAVYWEIGHT;
907 } else {
908 /*
909 * If this function is a named statement function not at top-level
910 * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
911 * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
912 * enclosing function, if any, must be heavyweight.
913 */
914 if ((!lambda && funAtom && tc->topStmt) ||
915 (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
916 tc->flags |= TCF_FUN_HEAVYWEIGHT;
917 }
918 }
919 #endif
921 #if JS_HAS_LEXICAL_CLOSURE
922 if (lambda || !funAtom) {
923 /*
924 * ECMA ed. 3 standard: function expression, possibly anonymous (even
925 * if at top-level, an unnamed function is an expression statement, not
926 * a function declaration).
927 */
928 op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
929 } else if (tc->topStmt) {
930 /*
931 * ECMA ed. 3 extension: a function expression statement not at the
932 * top level, e.g., in a compound statement such as the "then" part
933 * of an "if" statement, binds a closure only if control reaches that
934 * sub-statement.
935 */
936 op = JSOP_CLOSURE;
937 } else
938 #endif
939 op = JSOP_NOP;
941 /*
942 * Absent use of the new scoped local GC roots API around compiler calls,
943 * we need to atomize here to protect against a GC activation. Atoms are
944 * protected from GC during compilation by the JS_FRIEND_API entry points
945 * in this file. There doesn't seem to be any gain in switching from the
946 * atom-keeping method to the bulkier, slower scoped local roots method.
947 */
948 pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
949 if (!pn->pn_funAtom)
950 return NULL;
952 pn->pn_op = op;
953 pn->pn_body = body;
954 pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;
955 pn->pn_tryCount = funtc.tryCount;
956 TREE_CONTEXT_FINISH(&funtc);
957 return pn;
958 }
960 static JSParseNode *
961 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
962 {
963 return FunctionDef(cx, ts, tc, JS_FALSE);
964 }
966 #if JS_HAS_LEXICAL_CLOSURE
967 static JSParseNode *
968 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
969 {
970 return FunctionDef(cx, ts, tc, JS_TRUE);
971 }
972 #endif
974 /*
975 * Parse the statements in a block, creating a TOK_LC node that lists the
976 * statements' trees. If called from block-parsing code, the caller must
977 * match { before and } after.
978 */
979 static JSParseNode *
980 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
981 {
982 JSParseNode *pn, *pn2;
983 JSTokenType tt;
985 CHECK_RECURSION();
987 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
988 if (!pn)
989 return NULL;
990 PN_INIT_LIST(pn);
992 ts->flags |= TSF_OPERAND;
993 while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
994 ts->flags &= ~TSF_OPERAND;
995 pn2 = Statement(cx, ts, tc);
996 if (!pn2)
997 return NULL;
998 ts->flags |= TSF_OPERAND;
1000 /* If compiling top-level statements, emit as we go to save space. */
1001 if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
1002 if (cx->fp->fun &&
1003 JS_HAS_STRICT_OPTION(cx) &&
1004 (tc->flags & TCF_RETURN_EXPR)) {
1005 /*
1006 * Check pn2 for lack of a final return statement if it is the
1007 * last statement in the block.
1008 */
1009 tt = js_PeekToken(cx, ts);
1010 if ((tt == TOK_EOF || tt == TOK_RC) &&
1011 !CheckFinalReturn(cx, ts, pn2)) {
1012 tt = TOK_ERROR;
1013 break;
1014 }
1016 /*
1017 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1018 * CheckFinalReturn again.
1019 */
1020 tc->flags &= ~TCF_RETURN_EXPR;
1021 }
1022 if (!js_FoldConstants(cx, pn2, tc) ||
1023 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1024 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1025 tt = TOK_ERROR;
1026 break;
1027 }
1028 RecycleTree(pn2, tc);
1029 } else {
1030 PN_APPEND(pn, pn2);
1031 }
1032 }
1033 ts->flags &= ~TSF_OPERAND;
1034 if (tt == TOK_ERROR)
1035 return NULL;
1037 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1038 return pn;
1039 }
1041 static JSParseNode *
1042 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1043 {
1044 JSParseNode *pn, *pn2;
1046 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1047 pn = Expr(cx, ts, tc);
1048 if (!pn)
1049 return NULL;
1050 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1052 /*
1053 * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1054 * greater precedence than ==.
1055 * XXX not ECMA, but documented in several books -- now a strict warning.
1056 */
1057 if (pn->pn_type == TOK_ASSIGN &&
1058 pn->pn_op == JSOP_NOP &&
1059 pn->pn_right->pn_type > TOK_EQOP)
1060 {
1061 JSBool rewrite = !JSVERSION_IS_ECMA(cx->version);
1062 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1063 JSREPORT_WARNING | JSREPORT_STRICT,
1064 JSMSG_EQUAL_AS_ASSIGN,
1065 rewrite
1066 ? "\nAssuming equality test"
1067 : "")) {
1068 return NULL;
1069 }
1070 if (rewrite) {
1071 pn->pn_type = TOK_EQOP;
1072 pn->pn_op = (JSOp)cx->jsop_eq;
1073 pn2 = pn->pn_left;
1074 switch (pn2->pn_op) {
1075 case JSOP_SETNAME:
1076 pn2->pn_op = JSOP_NAME;
1077 break;
1078 case JSOP_SETPROP:
1079 pn2->pn_op = JSOP_GETPROP;
1080 break;
1081 case JSOP_SETELEM:
1082 pn2->pn_op = JSOP_GETELEM;
1083 break;
1084 default:
1085 JS_ASSERT(0);
1086 }
1087 }
1088 }
1089 return pn;
1090 }
1092 static JSBool
1093 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1094 {
1095 JSAtom *label;
1096 #if JS_HAS_LABEL_STATEMENT
1097 JSTokenType tt;
1099 tt = js_PeekTokenSameLine(cx, ts);
1100 if (tt == TOK_ERROR)
1101 return JS_FALSE;
1102 if (tt == TOK_NAME) {
1103 (void) js_GetToken(cx, ts);
1104 label = CURRENT_TOKEN(ts).t_atom;
1105 } else {
1106 label = NULL;
1107 }
1108 #else
1109 label = NULL;
1110 #endif
1111 pn->pn_atom = label;
1112 return JS_TRUE;
1113 }
1115 #if JS_HAS_EXPORT_IMPORT
1116 static JSParseNode *
1117 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1118 {
1119 JSParseNode *pn, *pn2, *pn3;
1120 JSTokenType tt;
1122 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1123 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1124 if (!pn)
1125 return NULL;
1126 pn->pn_op = JSOP_NAME;
1127 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1128 pn->pn_expr = NULL;
1129 pn->pn_slot = -1;
1130 pn->pn_attrs = 0;
1132 ts->flags |= TSF_OPERAND;
1133 while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1134 ts->flags &= ~TSF_OPERAND;
1135 if (pn->pn_op == JSOP_IMPORTALL)
1136 goto bad_import;
1138 if (tt == TOK_DOT) {
1139 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1140 if (!pn2)
1141 return NULL;
1142 if (js_MatchToken(cx, ts, TOK_STAR)) {
1143 pn2->pn_op = JSOP_IMPORTALL;
1144 pn2->pn_atom = NULL;
1145 } else {
1146 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1147 pn2->pn_op = JSOP_GETPROP;
1148 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1149 pn2->pn_slot = -1;
1150 pn2->pn_attrs = 0;
1151 }
1152 pn2->pn_expr = pn;
1153 pn2->pn_pos.begin = pn->pn_pos.begin;
1154 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1155 } else {
1156 /* Make a TOK_LB node. */
1157 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1158 if (!pn2)
1159 return NULL;
1160 pn3 = Expr(cx, ts, tc);
1161 if (!pn3)
1162 return NULL;
1164 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1165 pn2->pn_pos.begin = pn->pn_pos.begin;
1166 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1168 pn2->pn_op = JSOP_GETELEM;
1169 pn2->pn_left = pn;
1170 pn2->pn_right = pn3;
1171 }
1173 pn = pn2;
1174 ts->flags |= TSF_OPERAND;
1175 }
1176 ts->flags &= ~TSF_OPERAND;
1177 if (tt == TOK_ERROR)
1178 return NULL;
1179 js_UngetToken(ts);
1181 switch (pn->pn_op) {
1182 case JSOP_GETPROP:
1183 pn->pn_op = JSOP_IMPORTPROP;
1184 break;
1185 case JSOP_GETELEM:
1186 pn->pn_op = JSOP_IMPORTELEM;
1187 break;
1188 case JSOP_IMPORTALL:
1189 break;
1190 default:
1191 goto bad_import;
1192 }
1193 return pn;
1195 bad_import:
1196 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT);
1197 return NULL;
1198 }
1199 #endif /* JS_HAS_EXPORT_IMPORT */
1201 extern const char js_with_statement_str[];
1203 static JSParseNode *
1204 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1205 {
1206 JSTokenType tt;
1207 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1208 JSStmtInfo stmtInfo, *stmt, *stmt2;
1209 JSAtom *label;
1211 CHECK_RECURSION();
1213 ts->flags |= TSF_OPERAND;
1214 tt = js_GetToken(cx, ts);
1215 ts->flags &= ~TSF_OPERAND;
1217 #if JS_HAS_GETTER_SETTER
1218 if (tt == TOK_NAME) {
1219 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1220 if (tt == TOK_ERROR)
1221 return NULL;
1222 }
1223 #endif
1225 switch (tt) {
1226 #if JS_HAS_EXPORT_IMPORT
1227 case TOK_EXPORT:
1228 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1229 if (!pn)
1230 return NULL;
1231 PN_INIT_LIST(pn);
1232 if (js_MatchToken(cx, ts, TOK_STAR)) {
1233 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1234 if (!pn2)
1235 return NULL;
1236 PN_APPEND(pn, pn2);
1237 } else {
1238 do {
1239 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1240 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1241 if (!pn2)
1242 return NULL;
1243 pn2->pn_op = JSOP_NAME;
1244 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1245 pn2->pn_expr = NULL;
1246 pn2->pn_slot = -1;
1247 pn2->pn_attrs = 0;
1248 PN_APPEND(pn, pn2);
1249 } while (js_MatchToken(cx, ts, TOK_COMMA));
1250 }
1251 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1252 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1253 break;
1255 case TOK_IMPORT:
1256 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1257 if (!pn)
1258 return NULL;
1259 PN_INIT_LIST(pn);
1260 do {
1261 pn2 = ImportExpr(cx, ts, tc);
1262 if (!pn2)
1263 return NULL;
1264 PN_APPEND(pn, pn2);
1265 } while (js_MatchToken(cx, ts, TOK_COMMA));
1266 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1267 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1268 break;
1269 #endif /* JS_HAS_EXPORT_IMPORT */
1271 case TOK_FUNCTION:
1272 return FunctionStmt(cx, ts, tc);
1274 case TOK_IF:
1275 /* An IF node has three kids: condition, then, and optional else. */
1276 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1277 if (!pn)
1278 return NULL;
1279 pn1 = Condition(cx, ts, tc);
1280 if (!pn1)
1281 return NULL;
1282 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1283 pn2 = Statement(cx, ts, tc);
1284 if (!pn2)
1285 return NULL;
1286 if (js_MatchToken(cx, ts, TOK_ELSE)) {
1287 stmtInfo.type = STMT_ELSE;
1288 pn3 = Statement(cx, ts, tc);
1289 if (!pn3)
1290 return NULL;
1291 pn->pn_pos.end = pn3->pn_pos.end;
1292 } else {
1293 pn3 = NULL;
1294 pn->pn_pos.end = pn2->pn_pos.end;
1295 }
1296 js_PopStatement(tc);
1297 pn->pn_kid1 = pn1;
1298 pn->pn_kid2 = pn2;
1299 pn->pn_kid3 = pn3;
1300 return pn;
1302 #if JS_HAS_SWITCH_STATEMENT
1303 case TOK_SWITCH:
1304 {
1305 JSParseNode *pn5;
1306 JSBool seenDefault = JS_FALSE;
1308 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1309 if (!pn)
1310 return NULL;
1311 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1313 /* pn1 points to the switch's discriminant. */
1314 pn1 = Expr(cx, ts, tc);
1315 if (!pn1)
1316 return NULL;
1318 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1319 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1321 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1322 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1323 if (!pn2)
1324 return NULL;
1325 PN_INIT_LIST(pn2);
1327 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1329 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1330 switch (tt) {
1331 case TOK_DEFAULT:
1332 if (seenDefault) {
1333 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1334 JSMSG_TOO_MANY_DEFAULTS);
1335 return NULL;
1336 }
1337 seenDefault = JS_TRUE;
1338 /* fall through */
1340 case TOK_CASE:
1341 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1342 if (!pn3)
1343 return NULL;
1344 if (tt == TOK_DEFAULT) {
1345 pn3->pn_left = NULL;
1346 } else {
1347 pn3->pn_left = Expr(cx, ts, tc);
1348 if (!pn3->pn_left)
1349 return NULL;
1350 }
1351 PN_APPEND(pn2, pn3);
1352 if (pn2->pn_count == JS_BIT(16)) {
1353 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1354 JSMSG_TOO_MANY_CASES);
1355 return NULL;
1356 }
1357 break;
1359 case TOK_ERROR:
1360 return NULL;
1362 default:
1363 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1364 JSMSG_BAD_SWITCH);
1365 return NULL;
1366 }
1367 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1369 pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1370 if (!pn4)
1371 return NULL;
1372 pn4->pn_type = TOK_LC;
1373 PN_INIT_LIST(pn4);
1374 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1375 tt != TOK_CASE && tt != TOK_DEFAULT) {
1376 if (tt == TOK_ERROR)
1377 return NULL;
1378 pn5 = Statement(cx, ts, tc);
1379 if (!pn5)
1380 return NULL;
1381 pn4->pn_pos.end = pn5->pn_pos.end;
1382 PN_APPEND(pn4, pn5);
1383 }
1385 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1386 if (pn4->pn_head)
1387 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1388 pn3->pn_pos.end = pn4->pn_pos.end;
1389 pn3->pn_right = pn4;
1390 }
1392 js_PopStatement(tc);
1394 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1395 pn->pn_kid1 = pn1;
1396 pn->pn_kid2 = pn2;
1397 return pn;
1398 }
1399 #endif /* JS_HAS_SWITCH_STATEMENT */
1401 case TOK_WHILE:
1402 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1403 if (!pn)
1404 return NULL;
1405 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
1406 pn2 = Condition(cx, ts, tc);
1407 if (!pn2)
1408 return NULL;
1409 pn->pn_left = pn2;
1410 pn2 = Statement(cx, ts, tc);
1411 if (!pn2)
1412 return NULL;
1413 js_PopStatement(tc);
1414 pn->pn_pos.end = pn2->pn_pos.end;
1415 pn->pn_right = pn2;
1416 return pn;
1418 #if JS_HAS_DO_WHILE_LOOP
1419 case TOK_DO:
1420 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1421 if (!pn)
1422 return NULL;
1423 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
1424 pn2 = Statement(cx, ts, tc);
1425 if (!pn2)
1426 return NULL;
1427 pn->pn_left = pn2;
1428 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
1429 pn2 = Condition(cx, ts, tc);
1430 if (!pn2)
1431 return NULL;
1432 js_PopStatement(tc);
1433 pn->pn_pos.end = pn2->pn_pos.end;
1434 pn->pn_right = pn2;
1435 if (cx->version != JSVERSION_ECMA_3) {
1436 /*
1437 * All legacy and extended versions must do automatic semicolon
1438 * insertion after do-while. See the testcase and discussion in
1439 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
1440 */
1441 (void) js_MatchToken(cx, ts, TOK_SEMI);
1442 return pn;
1443 }
1444 break;
1445 #endif /* JS_HAS_DO_WHILE_LOOP */
1447 case TOK_FOR:
1448 /* A FOR node is binary, left is loop control and right is the body. */
1449 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1450 if (!pn)
1451 return NULL;
1452 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
1454 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
1455 ts->flags |= TSF_OPERAND;
1456 tt = js_PeekToken(cx, ts);
1457 ts->flags &= ~TSF_OPERAND;
1458 if (tt == TOK_SEMI) {
1459 /* No initializer -- set first kid of left sub-node to null. */
1460 pn1 = NULL;
1461 } else {
1462 /* Set pn1 to a var list or an initializing expression. */
1463 #if JS_HAS_IN_OPERATOR
1464 /*
1465 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
1466 * of the for statement. This flag will be used by the RelExpr
1467 * production; if it is set, then the 'in' keyword will not be
1468 * recognized as an operator, leaving it available to be parsed as
1469 * part of a for/in loop. A side effect of this restriction is
1470 * that (unparenthesized) expressions involving an 'in' operator
1471 * are illegal in the init clause of an ordinary for loop.
1472 */
1473 tc->flags |= TCF_IN_FOR_INIT;
1474 #endif /* JS_HAS_IN_OPERATOR */
1475 if (tt == TOK_VAR) {
1476 (void) js_GetToken(cx, ts);
1477 pn1 = Variables(cx, ts, tc);
1478 } else {
1479 pn1 = Expr(cx, ts, tc);
1480 }
1481 #if JS_HAS_IN_OPERATOR
1482 tc->flags &= ~TCF_IN_FOR_INIT;
1483 #endif /* JS_HAS_IN_OPERATOR */
1484 if (!pn1)
1485 return NULL;
1486 }
1488 /*
1489 * We can be sure that it's a for/in loop if there's still an 'in'
1490 * keyword here, even if JavaScript recognizes 'in' as an operator,
1491 * as we've excluded 'in' from being parsed in RelExpr by setting
1492 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
1493 */
1494 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
1495 stmtInfo.type = STMT_FOR_IN_LOOP;
1497 /* Check that the left side of the 'in' is valid. */
1498 if ((pn1->pn_type == TOK_VAR)
1499 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
1500 : (pn1->pn_type != TOK_NAME &&
1501 pn1->pn_type != TOK_DOT &&
1502 pn1->pn_type != TOK_LB)) {
1503 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1504 JSMSG_BAD_FOR_LEFTSIDE);
1505 return NULL;
1506 }
1508 if (pn1->pn_type == TOK_VAR) {
1509 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
1510 pn1->pn_extra |= PNX_FORINVAR;
1512 /* Generate a final POP only if the var has an initializer. */
1513 pn2 = pn1->pn_head;
1514 if (pn2->pn_expr)
1515 pn1->pn_extra |= PNX_POPVAR;
1516 } else {
1517 pn2 = pn1;
1518 }
1520 /* Beware 'for (arguments in ...)' with or without a 'var'. */
1521 if (pn2->pn_type == TOK_NAME &&
1522 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
1523 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1524 }
1526 /* Parse the object expression as the right operand of 'in'. */
1527 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
1528 if (!pn2)
1529 return NULL;
1530 pn->pn_left = pn2;
1531 } else {
1532 /* Parse the loop condition or null into pn2. */
1533 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
1534 ts->flags |= TSF_OPERAND;
1535 tt = js_PeekToken(cx, ts);
1536 ts->flags &= ~TSF_OPERAND;
1537 if (tt == TOK_SEMI) {
1538 pn2 = NULL;
1539 } else {
1540 pn2 = Expr(cx, ts, tc);
1541 if (!pn2)
1542 return NULL;
1543 }
1545 /* Parse the update expression or null into pn3. */
1546 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
1547 ts->flags |= TSF_OPERAND;
1548 tt = js_PeekToken(cx, ts);
1549 ts->flags &= ~TSF_OPERAND;
1550 if (tt == TOK_RP) {
1551 pn3 = NULL;
1552 } else {
1553 pn3 = Expr(cx, ts, tc);
1554 if (!pn3)
1555 return NULL;
1556 }
1558 /* Build the RESERVED node to use as the left kid of pn. */
1559 pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1560 if (!pn4)
1561 return NULL;
1562 pn4->pn_type = TOK_RESERVED;
1563 pn4->pn_op = JSOP_NOP;
1564 pn4->pn_kid1 = pn1;
1565 pn4->pn_kid2 = pn2;
1566 pn4->pn_kid3 = pn3;
1567 pn->pn_left = pn4;
1568 }
1570 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
1572 /* Parse the loop body into pn->pn_right. */
1573 pn2 = Statement(cx, ts, tc);
1574 if (!pn2)
1575 return NULL;
1576 pn->pn_right = pn2;
1577 js_PopStatement(tc);
1579 /* Record the absolute line number for source note emission. */
1580 pn->pn_pos.end = pn2->pn_pos.end;
1581 return pn;
1583 #if JS_HAS_EXCEPTIONS
1584 case TOK_TRY: {
1585 JSParseNode *catchtail = NULL;
1586 /*
1587 * try nodes are ternary.
1588 * kid1 is the try Statement
1589 * kid2 is the catch node
1590 * kid3 is the finally Statement
1591 *
1592 * catch nodes are ternary.
1593 * kid1 is the discriminant
1594 * kid2 is the next catch node, or NULL
1595 * kid3 is the catch block (on kid3 so that we can always append a
1596 * new catch pn on catchtail->kid2)
1597 *
1598 * catch discriminant nodes are binary
1599 * atom is the receptacle
1600 * expr is the discriminant code
1601 *
1602 * finally nodes are unary (just the finally expression)
1603 */
1604 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1605 pn->pn_op = JSOP_NOP;
1607 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
1608 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
1609 pn->pn_kid1 = Statements(cx, ts, tc);
1610 if (!pn->pn_kid1)
1611 return NULL;
1612 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
1613 js_PopStatement(tc);
1615 catchtail = pn;
1616 while (js_PeekToken(cx, ts) == TOK_CATCH) {
1617 /* check for another catch after unconditional catch */
1618 if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
1619 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1620 JSMSG_CATCH_AFTER_GENERAL);
1621 return NULL;
1622 }
1624 /*
1625 * legal catch forms are:
1626 * catch (v)
1627 * catch (v if <boolean_expression>)
1628 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
1629 */
1630 (void) js_GetToken(cx, ts); /* eat `catch' */
1631 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1632 if (!pn2)
1633 return NULL;
1635 /*
1636 * We use a PN_NAME for the discriminant (catchguard) node
1637 * with the actual discriminant code in the initializer spot
1638 */
1639 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
1640 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
1641 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1642 if (!pn3)
1643 return NULL;
1645 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
1646 pn3->pn_expr = NULL;
1647 #if JS_HAS_CATCH_GUARD
1648 /*
1649 * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
1650 * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
1651 */
1652 if (js_PeekToken(cx, ts) == TOK_IF) {
1653 (void)js_GetToken(cx, ts); /* eat `if' */
1654 pn3->pn_expr = Expr(cx, ts, tc);
1655 if (!pn3->pn_expr)
1656 return NULL;
1657 }
1658 #endif
1659 pn2->pn_kid1 = pn3;
1661 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
1663 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
1664 js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
1665 stmtInfo.label = pn3->pn_atom;
1666 pn2->pn_kid3 = Statements(cx, ts, tc);
1667 if (!pn2->pn_kid3)
1668 return NULL;
1669 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
1670 js_PopStatement(tc);
1672 catchtail = catchtail->pn_kid2 = pn2;
1673 }
1674 catchtail->pn_kid2 = NULL;
1676 if (js_MatchToken(cx, ts, TOK_FINALLY)) {
1677 tc->tryCount++;
1678 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
1679 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
1680 pn->pn_kid3 = Statements(cx, ts, tc);
1681 if (!pn->pn_kid3)
1682 return NULL;
1683 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
1684 js_PopStatement(tc);
1685 } else {
1686 pn->pn_kid3 = NULL;
1687 }
1688 if (!pn->pn_kid2 && !pn->pn_kid3) {
1689 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1690 JSMSG_CATCH_OR_FINALLY);
1691 return NULL;
1692 }
1693 tc->tryCount++;
1694 return pn;
1695 }
1697 case TOK_THROW:
1698 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1699 if (!pn)
1700 return NULL;
1702 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
1703 ts->flags |= TSF_OPERAND;
1704 tt = js_PeekTokenSameLine(cx, ts);
1705 ts->flags &= ~TSF_OPERAND;
1706 if (tt == TOK_ERROR)
1707 return NULL;
1708 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
1709 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1710 JSMSG_SYNTAX_ERROR);
1711 return NULL;
1712 }
1714 pn2 = Expr(cx, ts, tc);
1715 if (!pn2)
1716 return NULL;
1717 pn->pn_pos.end = pn2->pn_pos.end;
1718 pn->pn_op = JSOP_THROW;
1719 pn->pn_kid = pn2;
1720 break;
1722 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
1723 case TOK_CATCH:
1724 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1725 JSMSG_CATCH_WITHOUT_TRY);
1726 return NULL;
1728 case TOK_FINALLY:
1729 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1730 JSMSG_FINALLY_WITHOUT_TRY);
1731 return NULL;
1733 #endif /* JS_HAS_EXCEPTIONS */
1735 case TOK_BREAK:
1736 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1737 if (!pn)
1738 return NULL;
1739 if (!MatchLabel(cx, ts, pn))
1740 return NULL;
1741 stmt = tc->topStmt;
1742 label = pn->pn_atom;
1743 if (label) {
1744 for (; ; stmt = stmt->down) {
1745 if (!stmt) {
1746 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1747 JSMSG_LABEL_NOT_FOUND);
1748 return NULL;
1749 }
1750 if (stmt->type == STMT_LABEL && stmt->label == label)
1751 break;
1752 }
1753 } else {
1754 for (; ; stmt = stmt->down) {
1755 if (!stmt) {
1756 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1757 JSMSG_TOUGH_BREAK);
1758 return NULL;
1759 }
1760 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
1761 break;
1762 }
1763 }
1764 if (label)
1765 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1766 break;
1768 case TOK_CONTINUE:
1769 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1770 if (!pn)
1771 return NULL;
1772 if (!MatchLabel(cx, ts, pn))
1773 return NULL;
1774 stmt = tc->topStmt;
1775 label = pn->pn_atom;
1776 if (label) {
1777 for (stmt2 = NULL; ; stmt = stmt->down) {
1778 if (!stmt) {
1779 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1780 JSMSG_LABEL_NOT_FOUND);
1781 return NULL;
1782 }
1783 if (stmt->type == STMT_LABEL) {
1784 if (stmt->label == label) {
1785 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
1786 js_ReportCompileErrorNumber(cx, ts, NULL,
1787 JSREPORT_ERROR,
1788 JSMSG_BAD_CONTINUE);
1789 return NULL;
1790 }
1791 break;
1792 }
1793 } else {
1794 stmt2 = stmt;
1795 }
1796 }
1797 } else {
1798 for (; ; stmt = stmt->down) {
1799 if (!stmt) {
1800 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1801 JSMSG_BAD_CONTINUE);
1802 return NULL;
1803 }
1804 if (STMT_IS_LOOP(stmt))
1805 break;
1806 }
1807 }
1808 if (label)
1809 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1810 break;
1812 case TOK_WITH:
1813 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1814 JSREPORT_WARNING | JSREPORT_STRICT,
1815 JSMSG_DEPRECATED_USAGE,
1816 js_with_statement_str)) {
1817 return NULL;
1818 }
1820 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1821 if (!pn)
1822 return NULL;
1823 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
1824 pn2 = Expr(cx, ts, tc);
1825 if (!pn2)
1826 return NULL;
1827 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
1828 pn->pn_left = pn2;
1830 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
1831 pn2 = Statement(cx, ts, tc);
1832 if (!pn2)
1833 return NULL;
1834 js_PopStatement(tc);
1836 pn->pn_pos.end = pn2->pn_pos.end;
1837 pn->pn_right = pn2;
1838 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1839 return pn;
1841 case TOK_VAR:
1842 pn = Variables(cx, ts, tc);
1843 if (!pn)
1844 return NULL;
1846 /* Tell js_EmitTree to generate a final POP. */
1847 pn->pn_extra |= PNX_POPVAR;
1848 break;
1850 case TOK_RETURN:
1851 if (!(tc->flags & TCF_IN_FUNCTION)) {
1852 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1853 JSMSG_BAD_RETURN);
1854 return NULL;
1855 }
1856 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1857 if (!pn)
1858 return NULL;
1860 /* This is ugly, but we don't want to require a semicolon. */
1861 ts->flags |= TSF_OPERAND;
1862 tt = js_PeekTokenSameLine(cx, ts);
1863 ts->flags &= ~TSF_OPERAND;
1864 if (tt == TOK_ERROR)
1865 return NULL;
1867 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1868 pn2 = Expr(cx, ts, tc);
1869 if (!pn2)
1870 return NULL;
1871 tc->flags |= TCF_RETURN_EXPR;
1872 pn->pn_pos.end = pn2->pn_pos.end;
1873 pn->pn_kid = pn2;
1874 } else {
1875 tc->flags |= TCF_RETURN_VOID;
1876 pn->pn_kid = NULL;
1877 }
1879 if (JS_HAS_STRICT_OPTION(cx) &&
1880 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
1881 /*
1882 * We must be in a frame with a non-native function, because
1883 * we're compiling one.
1884 */
1885 if (!ReportNoReturnValue(cx, ts))
1886 return NULL;
1887 }
1888 break;
1890 case TOK_LC:
1891 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
1892 pn = Statements(cx, ts, tc);
1893 if (!pn)
1894 return NULL;
1896 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
1897 js_PopStatement(tc);
1898 return pn;
1900 case TOK_EOL:
1901 case TOK_SEMI:
1902 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1903 if (!pn)
1904 return NULL;
1905 pn->pn_type = TOK_SEMI;
1906 pn->pn_kid = NULL;
1907 return pn;
1909 #if JS_HAS_DEBUGGER_KEYWORD
1910 case TOK_DEBUGGER:
1911 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1912 if (!pn)
1913 return NULL;
1914 pn->pn_type = TOK_DEBUGGER;
1915 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1916 break;
1917 #endif /* JS_HAS_DEBUGGER_KEYWORD */
1919 case TOK_ERROR:
1920 return NULL;
1922 default:
1923 js_UngetToken(ts);
1924 pn2 = Expr(cx, ts, tc);
1925 if (!pn2)
1926 return NULL;
1928 if (js_PeekToken(cx, ts) == TOK_COLON) {
1929 if (pn2->pn_type != TOK_NAME) {
1930 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1931 JSMSG_BAD_LABEL);
1932 return NULL;
1933 }
1934 label = pn2->pn_atom;
1935 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1936 if (stmt->type == STMT_LABEL && stmt->label == label) {
1937 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1938 JSMSG_DUPLICATE_LABEL);
1939 return NULL;
1940 }
1941 }
1942 (void) js_GetToken(cx, ts);
1944 /* Push a label struct and parse the statement. */
1945 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
1946 stmtInfo.label = label;
1947 pn = Statement(cx, ts, tc);
1948 if (!pn)
1949 return NULL;
1951 /* Pop the label, set pn_expr, and return early. */
1952 js_PopStatement(tc);
1953 pn2->pn_type = TOK_COLON;
1954 pn2->pn_pos.end = pn->pn_pos.end;
1955 pn2->pn_expr = pn;
1956 return pn2;
1957 }
1959 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
1960 if (!pn)
1961 return NULL;
1962 pn->pn_type = TOK_SEMI;
1963 pn->pn_pos = pn2->pn_pos;
1964 pn->pn_kid = pn2;
1965 break;
1966 }
1968 /* Check termination of this primitive statement. */
1969 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
1970 tt = js_PeekTokenSameLine(cx, ts);
1971 if (tt == TOK_ERROR)
1972 return NULL;
1973 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
1974 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1975 JSMSG_SEMI_BEFORE_STMNT);
1976 return NULL;
1977 }
1978 }
1980 (void) js_MatchToken(cx, ts, TOK_SEMI);
1981 return pn;
1982 }
1984 static JSParseNode *
1985 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1986 {
1987 JSParseNode *pn, *pn2;
1988 JSObject *obj, *pobj;
1989 JSStackFrame *fp;
1990 JSFunction *fun;
1991 JSClass *clasp;
1992 JSPropertyOp getter, setter, currentGetter, currentSetter;
1993 JSAtom *atom;
1994 JSAtomListElement *ale;
1995 JSOp prevop;
1996 JSProperty *prop;
1997 JSScopeProperty *sprop;
1998 JSBool ok;
2000 /*
2001 * The tricky part of this code is to create special parsenode opcodes for
2002 * getting and setting variables (which will be stored as special slots in
2003 * the frame). The complex special case is an eval() inside a function.
2004 * If the evaluated string references variables in the enclosing function,
2005 * then we need to generate the special variable opcodes. We determine
2006 * this by looking up the variable id in the current variable scope.
2007 */
2008 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
2009 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2010 if (!pn)
2011 return NULL;
2012 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2013 pn->pn_extra = 0; /* assume no JSOP_POP needed */
2014 PN_INIT_LIST(pn);
2016 /*
2017 * Skip eval and debugger frames when looking for the function whose code
2018 * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION
2019 * will be set in tc->flags, and we can be sure fp->fun is the function to
2020 * use. But if a function calls eval, the string argument is treated as a
2021 * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
2022 *
2023 * What's more, when the following code is reached from eval, cx->fp->fun
2024 * is eval's JSFunction (a native function), so we need to skip its frame.
2025 * We should find the scripted caller's function frame just below it, but
2026 * we code a loop out of paranoia.
2027 */
2028 for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
2029 continue;
2030 obj = fp->varobj;
2031 fun = fp->fun;
2032 clasp = OBJ_GET_CLASS(cx, obj);
2033 if (fun && clasp == &js_FunctionClass) {
2034 /* We are compiling code inside a function */
2035 getter = js_GetLocalVariable;
2036 setter = js_SetLocalVariable;
2037 } else if (fun && clasp == &js_CallClass) {
2038 /* We are compiling code from an eval inside a function */
2039 getter = js_GetCallVariable;
2040 setter = js_SetCallVariable;
2041 } else {
2042 getter = clasp->getProperty;
2043 setter = clasp->setProperty;
2044 }
2046 ok = JS_TRUE;
2047 do {
2048 currentGetter = getter;
2049 currentSetter = setter;
2050 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2051 atom = CURRENT_TOKEN(ts).t_atom;
2053 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2054 if (ale) {
2055 prevop = ALE_JSOP(ale);
2056 if (JS_HAS_STRICT_OPTION(cx) ||
2057 pn->pn_op == JSOP_DEFCONST ||
2058 prevop == JSOP_DEFCONST) {
2059 const char *name = js_AtomToPrintableString(cx, atom);
2060 if (!name ||
2061 !js_ReportCompileErrorNumber(cx, ts, NULL,
2062 (pn->pn_op != JSOP_DEFCONST &&
2063 prevop != JSOP_DEFCONST)
2064 ? JSREPORT_WARNING |
2065 JSREPORT_STRICT
2066 : JSREPORT_ERROR,
2067 JSMSG_REDECLARED_VAR,
2068 (prevop == JSOP_DEFFUN ||
2069 prevop == JSOP_CLOSURE)
2070 ? js_function_str
2071 : (prevop == JSOP_DEFCONST)
2072 ? js_const_str
2073 : js_var_str,
2074 name)) {
2075 return NULL;
2076 }
2077 }
2078 if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2079 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2080 } else {
2081 ale = js_IndexAtom(cx, atom, &tc->decls);
2082 if (!ale)
2083 return NULL;
2084 }
2085 ALE_SET_JSOP(ale, pn->pn_op);
2087 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2088 if (!pn2)
2089 return NULL;
2090 pn2->pn_op = JSOP_NAME;
2091 pn2->pn_atom = atom;
2092 pn2->pn_expr = NULL;
2093 pn2->pn_slot = -1;
2094 pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2095 ? JSPROP_ENUMERATE | JSPROP_PERMANENT |
2096 JSPROP_READONLY
2097 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2098 PN_APPEND(pn, pn2);
2100 if (!fun) {
2101 prop = NULL; /* don't lookup global variables at compile time */
2102 } else {
2103 if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
2104 return NULL;
2105 }
2106 if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2107 sprop = (JSScopeProperty *)prop;
2108 if (sprop->getter == js_GetArgument) {
2109 const char *name = js_AtomToPrintableString(cx, atom);
2110 if (!name) {
2111 ok = JS_FALSE;
2112 } else if (pn->pn_op == JSOP_DEFCONST) {
2113 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2114 JSMSG_REDECLARED_PARAM,
2115 name);
2116 ok = JS_FALSE;
2117 } else {
2118 currentGetter = js_GetArgument;
2119 currentSetter = js_SetArgument;
2120 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
2121 JSREPORT_WARNING |
2122 JSREPORT_STRICT,
2123 JSMSG_VAR_HIDES_ARG,
2124 name);
2125 }
2126 } else {
2127 if (fun) {
2128 /* Not an argument, must be a redeclared local var. */
2129 if (clasp == &js_FunctionClass) {
2130 JS_ASSERT(sprop->getter == js_GetLocalVariable);
2131 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2132 sprop->shortid < fun->nvars);
2133 } else if (clasp == &js_CallClass) {
2134 if (sprop->getter == js_GetCallVariable) {
2135 /*
2136 * Referencing a variable introduced by a var
2137 * statement in the enclosing function. Check
2138 * that the slot number we have is in range.
2139 */
2140 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2141 sprop->shortid < fun->nvars);
2142 } else {
2143 /*
2144 * A variable introduced through another eval:
2145 * don't use the special getters and setters
2146 * since we can't allocate a slot in the frame.
2147 */
2148 currentGetter = sprop->getter;
2149 currentSetter = sprop->setter;
2150 }
2151 }
2153 /* Override the old getter and setter, to handle eval. */
2154 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2155 0, sprop->attrs,
2156 currentGetter,
2157 currentSetter);
2158 if (!sprop)
2159 ok = JS_FALSE;
2160 }
2161 }
2162 } else {
2163 /*
2164 * Property not found in current variable scope: we have not seen
2165 * this variable before. Define a new local variable by adding a
2166 * property to the function's scope, allocating one slot in the
2167 * function's frame. Global variables and any locals declared in
2168 * with statement bodies are handled at runtime, by script prolog
2169 * JSOP_DEFVAR bytecodes generated for slot-less vars.
2170 */
2171 sprop = NULL;
2172 if (prop) {
2173 OBJ_DROP_PROPERTY(cx, pobj, prop);
2174 prop = NULL;
2175 }
2176 if (currentGetter == js_GetCallVariable) {
2177 /* Can't increase fun->nvars in an active frame! */
2178 currentGetter = clasp->getProperty;
2179 currentSetter = clasp->setProperty;
2180 }
2181 if (currentGetter == js_GetLocalVariable &&
2182 atom != cx->runtime->atomState.argumentsAtom &&
2183 fp->scopeChain == obj &&
2184 !js_InWithStatement(tc)) {
2185 if (!js_AddNativeProperty(cx, obj, (jsid)atom,
2186 currentGetter, currentSetter,
2187 SPROP_INVALID_SLOT,
2188 pn2->pn_attrs | JSPROP_SHARED,
2189 SPROP_HAS_SHORTID, fun->nvars)) {
2190 ok = JS_FALSE;
2191 }
2192 fun->nvars++;
2193 }
2194 }
2196 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2197 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2198 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2199 JSMSG_BAD_VAR_INIT);
2200 ok = JS_FALSE;
2201 } else {
2202 pn2->pn_expr = AssignExpr(cx, ts, tc);
2203 if (!pn2->pn_expr) {
2204 ok = JS_FALSE;
2205 } else {
2206 pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2207 ? JSOP_SETCONST
2208 : JSOP_SETNAME;
2209 if (atom == cx->runtime->atomState.argumentsAtom)
2210 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2211 }
2212 }
2213 }
2215 if (prop)
2216 OBJ_DROP_PROPERTY(cx, pobj, prop);
2217 if (!ok)
2218 return NULL;
2219 } while (js_MatchToken(cx, ts, TOK_COMMA));
2221 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2222 return pn;
2223 }
2225 static JSParseNode *
2226 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2227 {
2228 JSParseNode *pn, *pn2;
2230 pn = AssignExpr(cx, ts, tc);
2231 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2232 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2233 if (!pn2)
2234 return NULL;
2235 pn2->pn_pos.begin = pn->pn_pos.begin;
2236 PN_INIT_LIST_1(pn2, pn);
2237 pn = pn2;
2238 do {
2239 pn2 = AssignExpr(cx, ts, tc);
2240 if (!pn2)
2241 return NULL;
2242 PN_APPEND(pn, pn2);
2243 } while (js_MatchToken(cx, ts, TOK_COMMA));
2244 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2245 }
2246 return pn;
2247 }
2249 static JSParseNode *
2250 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2251 {
2252 JSParseNode *pn, *pn2;
2253 JSTokenType tt;
2254 JSOp op;
2256 CHECK_RECURSION();
2258 pn = CondExpr(cx, ts, tc);
2259 if (!pn)
2260 return NULL;
2262 tt = js_GetToken(cx, ts);
2263 #if JS_HAS_GETTER_SETTER
2264 if (tt == TOK_NAME) {
2265 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2266 if (tt == TOK_ERROR)
2267 return NULL;
2268 }
2269 #endif
2270 if (tt != TOK_ASSIGN) {
2271 js_UngetToken(ts);
2272 return pn;
2273 }
2275 op = CURRENT_TOKEN(ts).t_op;
2276 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2277 continue;
2278 switch (pn2->pn_type) {
2279 case TOK_NAME:
2280 pn2->pn_op = JSOP_SETNAME;
2281 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2282 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2283 break;
2284 case TOK_DOT:
2285 pn2->pn_op = JSOP_SETPROP;
2286 break;
2287 case TOK_LB:
2288 pn2->pn_op = JSOP_SETELEM;
2289 break;
2290 #if JS_HAS_LVALUE_RETURN
2291 case TOK_LP:
2292 pn2->pn_op = JSOP_SETCALL;
2293 break;
2294 #endif
2295 default:
2296 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2297 JSMSG_BAD_LEFTSIDE_OF_ASS);
2298 return NULL;
2299 }
2300 pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
2301 return pn;
2302 }
2304 static JSParseNode *
2305 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2306 {
2307 JSParseNode *pn, *pn1, *pn2, *pn3;
2308 #if JS_HAS_IN_OPERATOR
2309 uintN oldflags;
2310 #endif /* JS_HAS_IN_OPERATOR */
2312 pn = OrExpr(cx, ts, tc);
2313 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
2314 pn1 = pn;
2315 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2316 if (!pn)
2317 return NULL;
2318 #if JS_HAS_IN_OPERATOR
2319 /*
2320 * Always accept the 'in' operator in the middle clause of a ternary,
2321 * where it's unambiguous, even if we might be parsing the init of a
2322 * for statement.
2323 */
2324 oldflags = tc->flags;
2325 tc->flags &= ~TCF_IN_FOR_INIT;
2326 #endif /* JS_HAS_IN_OPERATOR */
2327 pn2 = AssignExpr(cx, ts, tc);
2328 #if JS_HAS_IN_OPERATOR
2329 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
2330 #endif /* JS_HAS_IN_OPERATOR */
2332 if (!pn2)
2333 return NULL;
2334 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
2335 pn3 = AssignExpr(cx, ts, tc);
2336 if (!pn3)
2337 return NULL;
2338 pn->pn_pos.begin = pn1->pn_pos.begin;
2339 pn->pn_pos.end = pn3->pn_pos.end;
2340 pn->pn_kid1 = pn1;
2341 pn->pn_kid2 = pn2;
2342 pn->pn_kid3 = pn3;
2343 }
2344 return pn;
2345 }
2347 static JSParseNode *
2348 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2349 {
2350 JSParseNode *pn;
2352 pn = AndExpr(cx, ts, tc);
2353 if (pn && js_MatchToken(cx, ts, TOK_OR))
2354 pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
2355 return pn;
2356 }
2358 static JSParseNode *
2359 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2360 {
2361 JSParseNode *pn;
2363 pn = BitOrExpr(cx, ts, tc);
2364 if (pn && js_MatchToken(cx, ts, TOK_AND))
2365 pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
2366 return pn;
2367 }
2369 static JSParseNode *
2370 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2371 {
2372 JSParseNode *pn;
2374 pn = BitXorExpr(cx, ts, tc);
2375 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
2376 pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
2377 tc);
2378 }
2379 return pn;
2380 }
2382 static JSParseNode *
2383 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2384 {
2385 JSParseNode *pn;
2387 pn = BitAndExpr(cx, ts, tc);
2388 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
2389 pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
2390 tc);
2391 }
2392 return pn;
2393 }
2395 static JSParseNode *
2396 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2397 {
2398 JSParseNode *pn;
2400 pn = EqExpr(cx, ts, tc);
2401 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
2402 pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
2403 return pn;
2404 }
2406 static JSParseNode *
2407 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2408 {
2409 JSParseNode *pn;
2410 JSOp op;
2412 pn = RelExpr(cx, ts, tc);
2413 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
2414 op = CURRENT_TOKEN(ts).t_op;
2415 pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
2416 }
2417 return pn;
2418 }
2420 static JSParseNode *
2421 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2422 {
2423 JSParseNode *pn;
2424 JSTokenType tt;
2425 JSOp op;
2426 #if JS_HAS_IN_OPERATOR
2427 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
2429 /*
2430 * Uses of the in operator in ShiftExprs are always unambiguous,
2431 * so unset the flag that prohibits recognizing it.
2432 */
2433 tc->flags &= ~TCF_IN_FOR_INIT;
2434 #endif /* JS_HAS_IN_OPERATOR */
2436 pn = ShiftExpr(cx, ts, tc);
2437 while (pn &&
2438 (js_MatchToken(cx, ts, TOK_RELOP)
2439 #if JS_HAS_IN_OPERATOR
2440 /*
2441 * Recognize the 'in' token as an operator only if we're not
2442 * currently in the init expr of a for loop.
2443 */
2444 || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
2445 #endif /* JS_HAS_IN_OPERATOR */
2446 #if JS_HAS_INSTANCEOF
2447 || js_MatchToken(cx, ts, TOK_INSTANCEOF)
2448 #endif /* JS_HAS_INSTANCEOF */
2449 )) {
2450 tt = CURRENT_TOKEN(ts).type;
2451 op = CURRENT_TOKEN(ts).t_op;
2452 pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
2453 }
2454 #if JS_HAS_IN_OPERATOR
2455 /* Restore previous state of inForInit flag. */
2456 tc->flags |= inForInitFlag;
2457 #endif /* JS_HAS_IN_OPERATOR */
2459 return pn;
2460 }
2462 static JSParseNode *
2463 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2464 {
2465 JSParseNode *pn;
2466 JSOp op;
2468 pn = AddExpr(cx, ts, tc);
2469 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
2470 op = CURRENT_TOKEN(ts).t_op;
2471 pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
2472 }
2473 return pn;
2474 }
2476 static JSParseNode *
2477 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2478 {
2479 JSParseNode *pn;
2480 JSTokenType tt;
2481 JSOp op;
2483 pn = MulExpr(cx, ts, tc);
2484 while (pn &&
2485 (js_MatchToken(cx, ts, TOK_PLUS) ||
2486 js_MatchToken(cx, ts, TOK_MINUS))) {
2487 tt = CURRENT_TOKEN(ts).type;
2488 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
2489 pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
2490 }
2491 return pn;
2492 }
2494 static JSParseNode *
2495 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2496 {
2497 JSParseNode *pn;
2498 JSTokenType tt;
2499 JSOp op;
2501 pn = UnaryExpr(cx, ts, tc);
2502 while (pn &&
2503 (js_MatchToken(cx, ts, TOK_STAR) ||
2504 js_MatchToken(cx, ts, TOK_DIVOP))) {
2505 tt = CURRENT_TOKEN(ts).type;
2506 op = CURRENT_TOKEN(ts).t_op;
2507 pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
2508 }
2509 return pn;
2510 }
2512 static JSParseNode *
2513 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
2514 const char *name)
2515 {
2516 while (kid->pn_type == TOK_RP)
2517 kid = kid->pn_kid;
2518 if (kid->pn_type != TOK_NAME &&
2519 kid->pn_type != TOK_DOT &&
2520 #if JS_HAS_LVALUE_RETURN
2521 (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
2522 #endif
2523 kid->pn_type != TOK_LB) {
2524 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2525 JSMSG_BAD_OPERAND, name);
2526 return NULL;
2527 }
2528 pn->pn_kid = kid;
2529 return kid;
2530 }
2532 static const char *incop_name_str[] = {"increment", "decrement"};
2534 static JSBool
2535 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2536 JSParseNode *pn, JSParseNode *kid,
2537 JSTokenType tt, JSBool preorder)
2538 {
2539 JSOp op;
2541 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
2542 if (!kid)
2543 return JS_FALSE;
2544 switch (kid->pn_type) {
2545 case TOK_NAME:
2546 op = (tt == TOK_INC)
2547 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
2548 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
2549 if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
2550 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2551 break;
2553 case TOK_DOT:
2554 op = (tt == TOK_INC)
2555 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
2556 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
2557 break;
2559 #if JS_HAS_LVALUE_RETURN
2560 case TOK_LP:
2561 kid->pn_op = JSOP_SETCALL;
2562 #endif
2563 case TOK_LB:
2564 op = (tt == TOK_INC)
2565 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
2566 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
2567 break;
2569 default:
2570 JS_ASSERT(0);
2571 op = JSOP_NOP;
2572 }
2573 pn->pn_op = op;
2574 return JS_TRUE;
2575 }
2577 static JSParseNode *
2578 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2579 {
2580 JSTokenType tt;
2581 JSParseNode *pn, *pn2;
2583 ts->flags |= TSF_OPERAND;
2584 tt = js_GetToken(cx, ts);
2585 ts->flags &= ~TSF_OPERAND;
2587 switch (tt) {
2588 case TOK_UNARYOP:
2589 case TOK_PLUS:
2590 case TOK_MINUS:
2591 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2592 if (!pn)
2593 return NULL;
2594 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
2595 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2596 pn2 = UnaryExpr(cx, ts, tc);
2597 if (!pn2)
2598 return NULL;
2599 pn->pn_pos.end = pn2->pn_pos.end;
2600 pn->pn_kid = pn2;
2601 break;
2603 case TOK_INC:
2604 case TOK_DEC:
2605 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2606 if (!pn)
2607 return NULL;
2608 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
2609 if (!pn2)
2610 return NULL;
2611 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
2612 return NULL;
2613 pn->pn_pos.end = pn2->pn_pos.end;
2614 break;
2616 case TOK_DELETE:
2617 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2618 if (!pn)
2619 return NULL;
2620 pn2 = UnaryExpr(cx, ts, tc);
2621 if (!pn2)
2622 return NULL;
2623 pn->pn_pos.end = pn2->pn_pos.end;
2625 /*
2626 * Under ECMA3, deleting any unary expression is valid -- it simply
2627 * returns true. Here we strip off any parentheses.
2628 */
2629 while (pn2->pn_type == TOK_RP)
2630 pn2 = pn2->pn_kid;
2631 pn->pn_kid = pn2;
2632 break;
2634 case TOK_ERROR:
2635 return NULL;
2637 default:
2638 js_UngetToken(ts);
2639 pn = MemberExpr(cx, ts, tc, JS_TRUE);
2640 if (!pn)
2641 return NULL;
2643 /* Don't look across a newline boundary for a postfix incop. */
2644 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2645 tt = js_PeekTokenSameLine(cx, ts);
2646 if (tt == TOK_INC || tt == TOK_DEC) {
2647 (void) js_GetToken(cx, ts);
2648 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2649 if (!pn2)
2650 return NULL;
2651 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
2652 return NULL;
2653 pn2->pn_pos.begin = pn->pn_pos.begin;
2654 pn = pn2;
2655 }
2656 }
2657 break;
2658 }
2659 return pn;
2660 }
2662 static JSBool
2663 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2664 JSParseNode *listNode)
2665 {
2666 JSBool matched;
2668 ts->flags |= TSF_OPERAND;
2669 matched = js_MatchToken(cx, ts, TOK_RP);
2670 ts->flags &= ~TSF_OPERAND;
2671 if (!matched) {
2672 do {
2673 JSParseNode *argNode = AssignExpr(cx, ts, tc);
2674 if (!argNode)
2675 return JS_FALSE;
2676 PN_APPEND(listNode, argNode);
2677 } while (js_MatchToken(cx, ts, TOK_COMMA));
2679 if (js_GetToken(cx, ts) != TOK_RP) {
2680 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2681 JSMSG_PAREN_AFTER_ARGS);
2682 return JS_FALSE;
2683 }
2684 }
2685 return JS_TRUE;
2686 }
2688 static JSParseNode *
2689 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
2690 JSBool allowCallSyntax)
2691 {
2692 JSParseNode *pn, *pn2, *pn3;
2693 JSTokenType tt;
2695 CHECK_RECURSION();
2697 /* Check for new expression first. */
2698 ts->flags |= TSF_OPERAND;
2699 tt = js_PeekToken(cx, ts);
2700 ts->flags &= ~TSF_OPERAND;
2701 if (tt == TOK_NEW) {
2702 (void) js_GetToken(cx, ts);
2704 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2705 if (!pn)
2706 return NULL;
2707 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
2708 if (!pn2)
2709 return NULL;
2710 pn->pn_op = JSOP_NEW;
2711 PN_INIT_LIST_1(pn, pn2);
2712 pn->pn_pos.begin = pn2->pn_pos.begin;
2714 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
2715 return NULL;
2716 if (pn->pn_count > ARGC_LIMIT) {
2717 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2718 JSMSG_TOO_MANY_CON_ARGS);
2719 return NULL;
2720 }
2721 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2722 } else {
2723 pn = PrimaryExpr(cx, ts, tc);
2724 if (!pn)
2725 return NULL;
2726 }
2728 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
2729 if (tt == TOK_DOT) {
2730 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2731 if (!pn2)
2732 return NULL;
2733 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
2734 pn2->pn_pos.begin = pn->pn_pos.begin;
2735 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2736 pn2->pn_op = JSOP_GETPROP;
2737 pn2->pn_expr = pn;
2738 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
2739 } else if (tt == TOK_LB) {
2740 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2741 if (!pn2)
2742 return NULL;
2743 pn3 = Expr(cx, ts, tc);
2744 if (!pn3)
2745 return NULL;
2747 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
2748 pn2->pn_pos.begin = pn->pn_pos.begin;
2749 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2751 /* Optimize o['p'] to o.p by rewriting pn2. */
2752 if (pn3->pn_type == TOK_STRING) {
2753 pn2->pn_type = TOK_DOT;
2754 pn2->pn_op = JSOP_GETPROP;
2755 pn2->pn_arity = PN_NAME;
2756 pn2->pn_expr = pn;
2757 pn2->pn_atom = pn3->pn_atom;
2758 } else {
2759 pn2->pn_op = JSOP_GETELEM;
2760 pn2->pn_left = pn;
2761 pn2->pn_right = pn3;
2762 }
2763 } else if (allowCallSyntax && tt == TOK_LP) {
2764 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2765 if (!pn2)
2766 return NULL;
2768 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
2769 pn2->pn_op = JSOP_CALL;
2770 if (pn->pn_op == JSOP_NAME &&
2771 pn->pn_atom == cx->runtime->atomState.evalAtom) {
2772 pn2->pn_op = JSOP_EVAL;
2773 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2774 }
2776 PN_INIT_LIST_1(pn2, pn);
2777 pn2->pn_pos.begin = pn->pn_pos.begin;
2779 if (!ArgumentList(cx, ts, tc, pn2))
2780 return NULL;
2781 if (pn2->pn_count > ARGC_LIMIT) {
2782 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2783 JSMSG_TOO_MANY_FUN_ARGS);
2784 return NULL;
2785 }
2786 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2787 } else {
2788 js_UngetToken(ts);
2789 return pn;
2790 }
2792 pn = pn2;
2793 }
2794 if (tt == TOK_ERROR)
2795 return NULL;
2796 return pn;
2797 }
2799 static JSParseNode *
2800 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2801 {
2802 JSTokenType tt;
2803 JSParseNode *pn, *pn2, *pn3;
2804 char *badWord;
2805 #if JS_HAS_GETTER_SETTER
2806 JSAtom *atom;
2807 JSRuntime *rt;
2808 #endif
2810 #if JS_HAS_SHARP_VARS
2811 JSParseNode *defsharp;
2812 JSBool notsharp;
2814 defsharp = NULL;
2815 notsharp = JS_FALSE;
2816 again:
2817 /*
2818 * Control flows here after #n= is scanned. If the following primary is
2819 * not valid after such a "sharp variable" definition, the tt switch case
2820 * should set notsharp.
2821 */
2822 #endif
2824 CHECK_RECURSION();
2826 ts->flags |= TSF_OPERAND;
2827 tt = js_GetToken(cx, ts);
2828 ts->flags &= ~TSF_OPERAND;
2830 #if JS_HAS_GETTER_SETTER
2831 if (tt == TOK_NAME) {
2832 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
2833 if (tt == TOK_ERROR)
2834 return NULL;
2835 }
2836 #endif
2838 switch (tt) {
2839 #if JS_HAS_LEXICAL_CLOSURE
2840 case TOK_FUNCTION:
2841 pn = FunctionExpr(cx, ts, tc);
2842 if (!pn)
2843 return NULL;
2844 break;
2845 #endif
2847 #if JS_HAS_INITIALIZERS
2848 case TOK_LB:
2849 {
2850 JSBool matched;
2851 jsuint atomIndex;
2853 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2854 if (!pn)
2855 return NULL;
2856 pn->pn_type = TOK_RB;
2857 pn->pn_extra = 0;
2859 #if JS_HAS_SHARP_VARS
2860 if (defsharp) {
2861 PN_INIT_LIST_1(pn, defsharp);
2862 defsharp = NULL;
2863 } else
2864 #endif
2865 PN_INIT_LIST(pn);
2867 ts->flags |= TSF_OPERAND;
2868 matched = js_MatchToken(cx, ts, TOK_RB);
2869 ts->flags &= ~TSF_OPERAND;
2870 if (!matched) {
2871 for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {
2872 ts->flags |= TSF_OPERAND;
2873 tt = js_PeekToken(cx, ts);
2874 ts->flags &= ~TSF_OPERAND;
2875 if (tt == TOK_RB) {
2876 pn->pn_extra |= PNX_ENDCOMMA;
2877 break;
2878 }
2880 if (tt == TOK_COMMA) {
2881 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
2882 js_MatchToken(cx, ts, TOK_COMMA);
2883 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2884 } else {
2885 pn2 = AssignExpr(cx, ts, tc);
2886 }
2887 if (!pn2)
2888 return NULL;
2889 PN_APPEND(pn, pn2);
2891 if (tt != TOK_COMMA) {
2892 /* If we didn't already match TOK_COMMA in above case. */
2893 if (!js_MatchToken(cx, ts, TOK_COMMA))
2894 break;
2895 }
2896 }
2898 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
2899 }
2900 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2901 return pn;
2902 }
2904 case TOK_LC:
2905 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2906 if (!pn)
2907 return NULL;
2908 pn->pn_type = TOK_RC;
2910 #if JS_HAS_SHARP_VARS
2911 if (defsharp) {
2912 PN_INIT_LIST_1(pn, defsharp);
2913 defsharp = NULL;
2914 } else
2915 #endif
2916 PN_INIT_LIST(pn);
2918 if (!js_MatchToken(cx, ts, TOK_RC)) {
2919 do {
2920 JSOp op;
2922 tt = js_GetToken(cx, ts);
2923 switch (tt) {
2924 case TOK_NUMBER:
2925 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2926 if (pn3)
2927 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
2928 break;
2929 case TOK_NAME:
2930 #if JS_HAS_GETTER_SETTER
2931 atom = CURRENT_TOKEN(ts).t_atom;
2932 rt = cx->runtime;
2933 if (atom == rt->atomState.getAtom ||
2934 atom == rt->atomState.setAtom) {
2935 op = (atom == rt->atomState.getAtom)
2936 ? JSOP_GETTER
2937 : JSOP_SETTER;
2938 if (js_MatchToken(cx, ts, TOK_NAME)) {
2939 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME,
2940 tc);
2941 if (!pn3)
2942 return NULL;
2943 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2944 pn3->pn_expr = NULL;
2946 /* We have to fake a 'function' token here. */
2947 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
2948 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
2949 pn2 = FunctionExpr(cx, ts, tc);
2950 pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
2951 goto skip;
2952 }
2953 }
2954 /* else fall thru ... */
2955 #endif
2956 case TOK_STRING:
2957 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2958 if (pn3)
2959 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2960 break;
2961 case TOK_RC:
2962 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2963 JSREPORT_WARNING |
2964 JSREPORT_STRICT,
2965 JSMSG_TRAILING_COMMA)) {
2966 return NULL;
2967 }
2968 goto end_obj_init;
2969 default:
2970 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2971 JSMSG_BAD_PROP_ID);
2972 return NULL;
2973 }
2975 tt = js_GetToken(cx, ts);
2976 #if JS_HAS_GETTER_SETTER
2977 if (tt == TOK_NAME) {
2978 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
2979 if (tt == TOK_ERROR)
2980 return NULL;
2981 }
2982 #endif
2983 if (tt != TOK_COLON) {
2984 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2985 JSMSG_COLON_AFTER_ID);
2986 return NULL;
2987 }
2988 op = CURRENT_TOKEN(ts).t_op;
2989 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
2990 tc);
2991 #if JS_HAS_GETTER_SETTER
2992 skip:
2993 #endif
2994 if (!pn2)
2995 return NULL;
2996 PN_APPEND(pn, pn2);
2997 } while (js_MatchToken(cx, ts, TOK_COMMA));
2999 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
3000 }
3001 end_obj_init:
3002 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3003 return pn;
3005 #if JS_HAS_SHARP_VARS
3006 case TOK_DEFSHARP:
3007 if (defsharp)
3008 goto badsharp;
3009 defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3010 if (!defsharp)
3011 return NULL;
3012 defsharp->pn_kid = NULL;
3013 defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
3014 goto again;
3016 case TOK_USESHARP:
3017 /* Check for forward/dangling references at runtime, to allow eval. */
3018 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3019 if (!pn)
3020 return NULL;
3021 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
3022 notsharp = JS_TRUE;
3023 break;
3024 #endif /* JS_HAS_SHARP_VARS */
3025 #endif /* JS_HAS_INITIALIZERS */
3027 case TOK_LP:
3028 {
3029 #if JS_HAS_IN_OPERATOR
3030 uintN oldflags;
3031 #endif
3032 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3033 if (!pn)
3034 return NULL;
3035 #if JS_HAS_IN_OPERATOR
3036 /*
3037 * Always accept the 'in' operator in a parenthesized expression,
3038 * where it's unambiguous, even if we might be parsing the init of a
3039 * for statement.
3040 */
3041 oldflags = tc->flags;
3042 tc->flags &= ~TCF_IN_FOR_INIT;
3043 #endif
3044 pn2 = Expr(cx, ts, tc);
3045 #if JS_HAS_IN_OPERATOR
3046 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3047 #endif
3048 if (!pn2)
3049 return NULL;
3051 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
3052 pn->pn_type = TOK_RP;
3053 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3054 pn->pn_kid = pn2;
3055 break;
3056 }
3058 case TOK_STRING:
3059 #if JS_HAS_SHARP_VARS
3060 notsharp = JS_TRUE;
3061 #endif
3062 /* FALL THROUGH */
3063 case TOK_NAME:
3064 case TOK_OBJECT:
3065 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3066 if (!pn)
3067 return NULL;
3068 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3069 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3070 if (tt == TOK_NAME) {
3071 pn->pn_arity = PN_NAME;
3072 pn->pn_expr = NULL;
3073 pn->pn_slot = -1;
3074 pn->pn_attrs = 0;
3076 /* Unqualified __parent__ and __proto__ uses require activations. */
3077 if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
3078 pn->pn_atom == cx->runtime->atomState.protoAtom) {
3079 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3080 } else {
3081 JSAtomListElement *ale;
3082 JSStackFrame *fp;
3083 JSStmtInfo *stmt;
3085 /* Measure optimizable global variable uses. */
3086 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
3087 if (ale &&
3088 !(fp = cx->fp)->fun &&
3089 fp->scopeChain == fp->varobj &&
3090 !js_InWithStatement(tc) &&
3091 !js_InCatchBlock(tc, pn->pn_atom)) {
3092 tc->globalUses++;
3093 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
3094 if (STMT_IS_LOOP(stmt)) {
3095 tc->loopyGlobalUses++;
3096 break;
3097 }
3098 }
3099 }
3100 }
3101 }
3102 break;
3104 case TOK_NUMBER:
3105 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3106 if (!pn)
3107 return NULL;
3108 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
3109 #if JS_HAS_SHARP_VARS
3110 notsharp = JS_TRUE;
3111 #endif
3112 break;
3114 case TOK_PRIMARY:
3115 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3116 if (!pn)
3117 return NULL;
3118 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3119 #if JS_HAS_SHARP_VARS
3120 notsharp = JS_TRUE;
3121 #endif
3122 break;
3124 #if !JS_HAS_EXPORT_IMPORT
3125 case TOK_EXPORT:
3126 case TOK_IMPORT:
3127 #endif
3128 case TOK_RESERVED:
3129 badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr,
3130 (size_t) CURRENT_TOKEN(ts).pos.end.index
3131 - CURRENT_TOKEN(ts).pos.begin.index);
3132 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3133 JSMSG_RESERVED_ID, badWord);
3134 JS_free(cx, badWord);
3135 return NULL;
3137 case TOK_ERROR:
3138 /* The scanner or one of its subroutines reported the error. */
3139 return NULL;
3141 default:
3142 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3143 JSMSG_SYNTAX_ERROR);
3144 return NULL;
3145 }
3147 #if JS_HAS_SHARP_VARS
3148 if (defsharp) {
3149 if (notsharp) {
3150 badsharp:
3151 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3152 JSMSG_BAD_SHARP_VAR_DEF);
3153 return NULL;
3154 }
3155 defsharp->pn_kid = pn;
3156 return defsharp;
3157 }
3158 #endif
3159 return pn;
3160 }
3162 static JSBool
3163 ContainsVarStmt(JSParseNode *pn)
3164 {
3165 JSParseNode *pn2;
3167 if (!pn)
3168 return JS_FALSE;
3169 switch (pn->pn_arity) {
3170 case PN_LIST:
3171 if (pn->pn_type == TOK_VAR)
3172 return JS_TRUE;
3173 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3174 if (ContainsVarStmt(pn2))
3175 return JS_TRUE;
3176 }
3177 break;
3178 case PN_TERNARY:
3179 return ContainsVarStmt(pn->pn_kid1) ||
3180 ContainsVarStmt(pn->pn_kid2) ||
3181 ContainsVarStmt(pn->pn_kid3);
3182 case PN_BINARY:
3183 /*
3184 * Limit recursion if pn is a binary expression, which can't contain a
3185 * var statement.
3186 */
3187 if (pn->pn_op != JSOP_NOP)
3188 return JS_FALSE;
3189 return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
3190 case PN_UNARY:
3191 if (pn->pn_op != JSOP_NOP)
3192 return JS_FALSE;
3193 return ContainsVarStmt(pn->pn_kid);
3194 default:;
3195 }
3196 return JS_FALSE;
3197 }
3199 /*
3200 * Fold from one constant type to another.
3201 * XXX handles only strings and numbers for now
3202 */
3203 static JSBool
3204 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
3205 {
3206 if (pn->pn_type != type) {
3207 switch (type) {
3208 case TOK_NUMBER:
3209 if (pn->pn_type == TOK_STRING) {
3210 jsdouble d;
3211 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
3212 return JS_FALSE;
3213 pn->pn_dval = d;
3214 pn->pn_type = TOK_NUMBER;
3215 pn->pn_op = JSOP_NUMBER;
3216 }
3217 break;
3219 case TOK_STRING:
3220 if (pn->pn_type == TOK_NUMBER) {
3221 JSString *str = js_NumberToString(cx, pn->pn_dval);
3222 if (!str)
3223 return JS_FALSE;
3224 pn->pn_atom = js_AtomizeString(cx, str, 0);
3225 if (!pn->pn_atom)
3226 return JS_FALSE;
3227 pn->pn_type = TOK_STRING;
3228 pn->pn_op = JSOP_STRING;
3229 }
3230 break;
3232 default:;
3233 }
3234 }
3235 return JS_TRUE;
3236 }
3238 /*
3239 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
3240 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
3241 * a successful call to this function.
3242 */
3243 static JSBool
3244 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
3245 JSParseNode *pn, JSTreeContext *tc)
3246 {
3247 jsdouble d, d2;
3248 int32 i, j;
3249 uint32 u;
3251 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
3252 d = pn1->pn_dval;
3253 d2 = pn2->pn_dval;
3254 switch (op) {
3255 case JSOP_LSH:
3256 case JSOP_RSH:
3257 if (!js_DoubleToECMAInt32(cx, d, &i))
3258 return JS_FALSE;
3259 if (!js_DoubleToECMAInt32(cx, d2, &j))
3260 return JS_FALSE;
3261 j &= 31;
3262 d = (op == JSOP_LSH) ? i << j : i >> j;
3263 break;
3265 case JSOP_URSH:
3266 if (!js_DoubleToECMAUint32(cx, d, &u))
3267 return JS_FALSE;
3268 if (!js_DoubleToECMAInt32(cx, d2, &j))
3269 return JS_FALSE;
3270 j &= 31;
3271 d = u >> j;
3272 break;
3274 case JSOP_ADD:
3275 d += d2;
3276 break;
3278 case JSOP_SUB:
3279 d -= d2;
3280 break;
3282 case JSOP_MUL:
3283 d *= d2;
3284 break;
3286 case JSOP_DIV:
3287 if (d2 == 0) {
3288 #if defined(XP_WIN)
3289 /* XXX MSVC miscompiles such that (NaN == 0) */
3290 if (JSDOUBLE_IS_NaN(d2))
3291 d = *cx->runtime->jsNaN;
3292 else
3293 #endif
3294 if (d == 0 || JSDOUBLE_IS_NaN(d))
3295 d = *cx->runtime->jsNaN;
3296 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3297 d = *cx->runtime->jsNegativeInfinity;
3298 else
3299 d = *cx->runtime->jsPositiveInfinity;
3300 } else {
3301 d /= d2;
3302 }
3303 break;
3305 case JSOP_MOD:
3306 if (d2 == 0) {
3307 d = *cx->runtime->jsNaN;
3308 } else {
3309 #if defined(XP_WIN)
3310 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
3311 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
3312 #endif
3313 d = fmod(d, d2);
3314 }
3315 break;
3317 default:;
3318 }
3320 /* Take care to allow pn1 or pn2 to alias pn. */
3321 if (pn1 != pn)
3322 RecycleTree(pn1, tc);
3323 if (pn2 != pn)
3324 RecycleTree(pn2, tc);
3325 pn->pn_type = TOK_NUMBER;
3326 pn->pn_op = JSOP_NUMBER;
3327 pn->pn_arity = PN_NULLARY;
3328 pn->pn_dval = d;
3329 return JS_TRUE;
3330 }
3332 JSBool
3333 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
3334 {
3335 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
3336 int stackDummy;
3338 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
3339 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
3340 return JS_FALSE;
3341 }
3343 switch (pn->pn_arity) {
3344 case PN_FUNC:
3345 if (!js_FoldConstants(cx, pn->pn_body, tc))
3346 return JS_FALSE;
3347 break;
3349 case PN_LIST:
3350 /* Save the list head in pn1 for later use. */
3351 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3352 if (!js_FoldConstants(cx, pn2, tc))
3353 return JS_FALSE;
3354 }
3355 break;
3357 case PN_TERNARY:
3358 /* Any kid may be null (e.g. for (;;)). */
3359 pn1 = pn->pn_kid1;
3360 pn2 = pn->pn_kid2;
3361 pn3 = pn->pn_kid3;
3362 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3363 return JS_FALSE;
3364 if (pn2 && !js_FoldConstants(cx, pn2, tc))
3365 return JS_FALSE;
3366 if (pn3 && !js_FoldConstants(cx, pn3, tc))
3367 return JS_FALSE;
3368 break;
3370 case PN_BINARY:
3371 /* First kid may be null (for default case in switch). */
3372 pn1 = pn->pn_left;
3373 pn2 = pn->pn_right;
3374 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3375 return JS_FALSE;
3376 if (!js_FoldConstants(cx, pn2, tc))
3377 return JS_FALSE;
3378 break;
3380 case PN_UNARY:
3381 /* Our kid may be null (e.g. return; vs. return e;). */
3382 pn1 = pn->pn_kid;
3383 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3384 return JS_FALSE;
3385 break;
3387 case PN_NAME:
3388 /*
3389 * Skip pn1 down along a chain of dotted member expressions to avoid
3390 * excessive recursion. Our only goal here is to fold constants (if
3391 * any) in the primary expression operand to the left of the first
3392 * dot in the chain.
3393 */
3394 pn1 = pn->pn_expr;
3395 while (pn1 && pn1->pn_arity == PN_NAME)
3396 pn1 = pn1->pn_expr;
3397 if (pn1 && !js_FoldConstants(cx, pn1, tc))
3398 return JS_FALSE;
3399 break;
3401 case PN_NULLARY:
3402 break;
3403 }
3405 switch (pn->pn_type) {
3406 case TOK_IF:
3407 if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
3408 break;
3409 /* FALL THROUGH */
3411 case TOK_HOOK:
3412 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
3413 switch (pn1->pn_type) {
3414 case TOK_NUMBER:
3415 if (pn1->pn_dval == 0)
3416 pn2 = pn3;
3417 break;
3418 case TOK_STRING:
3419 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
3420 pn2 = pn3;
3421 break;
3422 case TOK_PRIMARY:
3423 if (pn1->pn_op == JSOP_TRUE)
3424 break;
3425 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
3426 pn2 = pn3;
3427 break;
3428 }
3429 /* FALL THROUGH */
3430 default:
3431 /* Early return to dodge common code that copies pn2 to pn. */
3432 return JS_TRUE;
3433 }
3435 if (pn2) {
3436 /* pn2 is the then- or else-statement subtree to compile. */
3437 PN_MOVE_NODE(pn, pn2);
3438 } else {
3439 /* False condition and no else: make pn an empty statement. */
3440 pn->pn_type = TOK_SEMI;
3441 pn->pn_arity = PN_UNARY;
3442 pn->pn_kid = NULL;
3443 }
3444 RecycleTree(pn2, tc);
3445 if (pn3 && pn3 != pn2)
3446 RecycleTree(pn3, tc);
3447 break;
3449 case TOK_PLUS:
3450 if (pn->pn_arity == PN_LIST) {
3451 size_t length, length2;
3452 jschar *chars;
3453 JSString *str, *str2;
3455 /*
3456 * Any string literal term with all others number or string means
3457 * this is a concatenation. If any term is not a string or number
3458 * literal, we can't fold.
3459 */
3460 JS_ASSERT(pn->pn_count > 2);
3461 if (pn->pn_extra & PNX_CANTFOLD)
3462 return JS_TRUE;
3463 if (pn->pn_extra != PNX_STRCAT)
3464 goto do_binary_op;
3466 /* Ok, we're concatenating: convert non-string constant operands. */
3467 length = 0;
3468 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3469 if (!FoldType(cx, pn2, TOK_STRING))
3470 return JS_FALSE;
3471 /* XXX fold only if all operands convert to string */
3472 if (pn2->pn_type != TOK_STRING)
3473 return JS_TRUE;
3474 length += ATOM_TO_STRING(pn2->pn_atom)->length;
3475 }
3477 /* Allocate a new buffer and string descriptor for the result. */
3478 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
3479 if (!chars)
3480 return JS_FALSE;
3481 str = js_NewString(cx, chars, length, 0);
3482 if (!str) {
3483 JS_free(cx, chars);
3484 return JS_FALSE;
3485 }
3487 /* Fill the buffer, advancing chars and recycling kids as we go. */
3488 for (pn2 = pn1; pn2; pn2 = pn3) {
3489 str2 = ATOM_TO_STRING(pn2->pn_atom);
3490 length2 = str2->length;
3491 js_strncpy(chars, str2->chars, length2);
3492 chars += length2;
3493 pn3 = pn2->pn_next;
3494 RecycleTree(pn2, tc);
3495 }
3496 *chars = 0;
3498 /* Atomize the result string and mutate pn to refer to it. */
3499 pn->pn_atom = js_AtomizeString(cx, str, 0);
3500 if (!pn->pn_atom)
3501 return JS_FALSE;
3502 pn->pn_type = TOK_STRING;
3503 pn->pn_op = JSOP_STRING;
3504 pn->pn_arity = PN_NULLARY;
3505 break;
3506 }
3508 /* Handle a binary string concatenation. */
3509 JS_ASSERT(pn->pn_arity == PN_BINARY);
3510 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
3511 JSString *left, *right, *str;
3513 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
3514 TOK_STRING)) {
3515 return JS_FALSE;
3516 }
3517 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
3518 return JS_TRUE;
3519 left = ATOM_TO_STRING(pn1->pn_atom);
3520 right = ATOM_TO_STRING(pn2->pn_atom);
3521 str = js_ConcatStrings(cx, left, right);
3522 if (!str)
3523 return JS_FALSE;
3524 pn->pn_atom = js_AtomizeString(cx, str, 0);
3525 if (!pn->pn_atom)
3526 return JS_FALSE;
3527 pn->pn_type = TOK_STRING;
3528 pn->pn_op = JSOP_STRING;
3529 pn->pn_arity = PN_NULLARY;
3530 RecycleTree(pn1, tc);
3531 RecycleTree(pn2, tc);
3532 break;
3533 }
3535 /* Can't concatenate string literals, let's try numbers. */
3536 goto do_binary_op;
3538 case TOK_STAR:
3539 /* The * in 'import *;' parses as a nullary star node. */
3540 if (pn->pn_arity == PN_NULLARY)
3541 break;
3542 /* FALL THROUGH */
3544 case TOK_SHOP:
3545 case TOK_MINUS:
3546 case TOK_DIVOP:
3547 do_binary_op:
3548 if (pn->pn_arity == PN_LIST) {
3549 JS_ASSERT(pn->pn_count > 2);
3550 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
3551 if (!FoldType(cx, pn2, TOK_NUMBER))
3552 return JS_FALSE;
3553 /* XXX fold only if all operands convert to number */
3554 if (pn2->pn_type != TOK_NUMBER)
3555 break;
3556 }
3557 if (!pn2) {
3558 JSOp op = pn->pn_op;
3560 pn2 = pn1->pn_next;
3561 pn3 = pn2->pn_next;
3562 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
3563 return JS_FALSE;
3564 while ((pn2 = pn3) != NULL) {
3565 pn3 = pn2->pn_next;
3566 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
3567 return JS_FALSE;
3568 }
3569 }
3570 } else {
3571 JS_ASSERT(pn->pn_arity == PN_BINARY);
3572 if (!FoldType(cx, pn1, TOK_NUMBER) ||
3573 !FoldType(cx, pn2, TOK_NUMBER)) {
3574 return JS_FALSE;
3575 }
3576 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
3577 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
3578 return JS_FALSE;
3579 }
3580 }
3581 break;
3583 case TOK_UNARYOP:
3584 if (pn1->pn_type == TOK_NUMBER) {
3585 jsdouble d;
3586 int32 i;
3588 /* Operate on one numeric constant. */
3589 d = pn1->pn_dval;
3590 switch (pn->pn_op) {
3591 case JSOP_BITNOT:
3592 if (!js_DoubleToECMAInt32(cx, d, &i))
3593 return JS_FALSE;
3594 d = ~i;
3595 break;
3597 case JSOP_NEG:
3598 #ifdef HPUX
3599 /*
3600 * Negation of a zero doesn't produce a negative
3601 * zero on HPUX. Perform the operation by bit
3602 * twiddling.
3603 */
3604 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
3605 #else
3606 d = -d;
3607 #endif
3608 break;
3610 case JSOP_POS:
3611 break;
3613 case JSOP_NOT:
3614 pn->pn_type = TOK_PRIMARY;
3615 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
3616 pn->pn_arity = PN_NULLARY;
3617 /* FALL THROUGH */
3619 default:
3620 /* Return early to dodge the common TOK_NUMBER code. */
3621 return JS_TRUE;
3622 }
3623 pn->pn_type = TOK_NUMBER;
3624 pn->pn_op = JSOP_NUMBER;
3625 pn->pn_arity = PN_NULLARY;
3626 pn->pn_dval = d;
3627 RecycleTree(pn1, tc);
3628 }
3629 break;
3631 default:;
3632 }
3634 return JS_TRUE;
3635 }