1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 /*
41 * JS parser.
42 *
43 * This is a recursive-descent parser for the JavaScript language specified by
44 * "The JavaScript 1.5 Language Specification". It uses lexical and semantic
45 * feedback to disambiguate non-LL(1) structures. It generates trees of nodes
46 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).
47 * After tree construction, it rewrites trees to fold constants and evaluate
48 * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
49 * generate bytecode.
50 *
51 * This parser attempts no error recovery. 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"
78
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 */
85
86 typedef JSParseNode *
87 JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
88
89 typedef JSParseNode *
90 JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
91 JSBool allowCallSyntax);
92
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;
116
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
128
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
138
139 #ifdef METER_PARSENODES
140 static uint32 parsenodes = 0;
141 static uint32 maxparsenodes = 0;
142 static uint32 recyclednodes = 0;
143 #endif
144
145 static void
RecycleTree(JSParseNode * pn,JSTreeContext * tc)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 }
157
158 static JSParseNode *
NewOrRecycledNode(JSContext * cx,JSTreeContext * tc)159 NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
160 {
161 JSParseNode *pn;
162
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;
170
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 }
207
208 /*
209 * Allocate a JSParseNode from cx's temporary arena.
210 */
211 static JSParseNode *
NewParseNode(JSContext * cx,JSToken * tok,JSParseNodeArity arity,JSTreeContext * tc)212 NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity,
213 JSTreeContext *tc)
214 {
215 JSParseNode *pn;
216
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 }
232
233 static JSParseNode *
NewBinary(JSContext * cx,JSTokenType tt,JSOp op,JSParseNode * left,JSParseNode * right,JSTreeContext * tc)234 NewBinary(JSContext *cx, JSTokenType tt,
235 JSOp op, JSParseNode *left, JSParseNode *right,
236 JSTreeContext *tc)
237 {
238 JSParseNode *pn, *pn1, *pn2;
239
240 if (!left || !right)
241 return NULL;
242
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 }
277
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 }
293
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 }
312
313 #if JS_HAS_GETTER_SETTER
314 static JSTokenType
CheckGetterOrSetter(JSContext * cx,JSTokenStream * ts,JSTokenType tt)315 CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
316 {
317 JSAtom *atom;
318 JSRuntime *rt;
319 JSOp op;
320 const char *name;
321
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
355
356 /*
357 * Parse a top-level JS script.
358 */
359 JS_FRIEND_API(JSParseNode *)
js_ParseTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts)360 js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
361 {
362 JSStackFrame *fp, frame;
363 JSTreeContext tc;
364 JSParseNode *pn;
365
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 }
382
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 }
405
406 TREE_CONTEXT_FINISH(&tc);
407 JS_UNKEEP_ATOMS(cx->runtime);
408 cx->fp = fp;
409 return pn;
410 }
411
412 static JSBool
FindLintIdentifier(JSContext * cx,jsid id,JSObject ** objp,JSProperty ** propp,jsval * val)413 FindLintIdentifier(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp, jsval *val)
414 {
415 *objp = NULL;
416 *propp = NULL;
417
418 if (cx->lint && cx->lint->scriptIdentifiers &&
419 !OBJ_LOOKUP_PROPERTY(cx, cx->lint->scriptIdentifiers, id, objp, propp)) {
420 return JS_FALSE;
421 }
422 if (*objp) {
423 if (val && !OBJ_GET_PROPERTY(cx, cx->lint->scriptIdentifiers, id, val))
424 return JS_FALSE;
425 return JS_TRUE;
426 }
427
428 if (cx->lint && cx->lint->dependencyList) {
429 JSLObjectList *cur = (JSLObjectList*)JS_LIST_HEAD(&cx->lint->dependencyList->links);
430 while (cur != cx->lint->dependencyList) {
431 /* look up property in dependency */
432 if (!OBJ_LOOKUP_PROPERTY(cx, cur->obj, id, objp, propp))
433 return JS_FALSE;
434 if (*objp) {
435 if (val && !OBJ_GET_PROPERTY(cx, cx->lint->scriptIdentifiers, id, val))
436 return JS_FALSE;
437 return JS_TRUE;
438 }
439 cur = (JSLObjectList*)JS_NEXT_LINK(&cur->links);
440 }
441 }
442
443 return JS_TRUE;
444 }
445
446 static JSBool
MarkIfDeclared(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * pn)447 MarkIfDeclared(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *pn)
448 {
449 JSAtomListElement *ale;
450 JSAtom *atom;
451 JSObject *obj, *pobj;
452 JSScopeProperty *sprop;
453 JSObject *scope;
454 JSTreeContext *curtc;
455 JSStackFrame *curframe;
456 JSStmtInfo *curstmt;
457
458 atom = pn->pn_atom;
459 scope = cx->fp->scopeChain;
460
461 /* nasty, but undeclared identifiers aren't warned against until end */
462 if (SHOULD_IGNORE_LINT_WARNINGS(cx)) {
463 pn->pn_attrs |= JSPROP_LINT_IGNORE;
464 return JS_TRUE;
465 }
466
467 /* must ignore all variables within "with" blocks */
468 curstmt = tc->topStmt;
469 while (curstmt) {
470 if (curstmt->type == STMT_WITH) {
471 pn->pn_attrs |= JSPROP_LINT_IGNORE;
472 return JS_TRUE;
473 }
474 curstmt = curstmt->down;
475 }
476
477 if (scope) {
478 /* Find the topmost object in the scope chain. */
479 do {
480 obj = scope;
481 scope = OBJ_GET_PARENT(cx, obj);
482 } while (scope);
483 } else {
484 obj = cx->globalObject;
485 }
486
487 if (obj && !OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))
488 return JS_FALSE;
489 if (pobj)
490 goto success;
491
492 curframe = cx->fp;
493 while (curframe) {
494 if (!OBJ_LOOKUP_PROPERTY(cx, curframe->varobj, (jsid)atom, &pobj, (JSProperty**)&sprop))
495 return JS_FALSE;
496 if (pobj)
497 goto success;
498 curframe = curframe->down;
499 }
500
501 curtc = tc;
502 while (curtc) {
503 ATOM_LIST_SEARCH(ale, &curtc->decls, atom);
504 if (ale)
505 goto success;
506 curtc = curtc->down;
507 }
508
509 curstmt = tc->topStmt;
510 while (curstmt) {
511 if (curstmt->label && curstmt->label == atom)
512 goto success;
513 curstmt = curstmt->down;
514 }
515
516 if (js_PeekToken(cx, ts) == TOK_COLON) {
517 /* assume that it's a label, which is actually a declaration */
518 goto success;
519 }
520
521 /* check for previous identifier */
522 if (!FindLintIdentifier(cx, (jsid)atom, &pobj, (JSProperty**)&sprop, NULL)) {
523 return JS_FALSE;
524 }
525 if (pobj)
526 goto success;
527
528 return JS_TRUE;
529
530 success:
531 pn->pn_attrs |= JSPROP_LINT_DECLARED;
532 return JS_TRUE;
533 }
534
535 static JSBool
WarnUndeclaredIdentifiers(JSCodeGenerator * cg,JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * pn,JSParseNode * parentNode,JSParseNode * grandParentNode)536 WarnUndeclaredIdentifiers(JSCodeGenerator *cg, JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
537 JSParseNode *pn, JSParseNode *parentNode, JSParseNode *grandParentNode)
538 {
539 JSParseNode *pn2;
540
541 if (!pn)
542 return JS_TRUE;
543
544 if (pn->pn_type == TOK_NAME && (pn->pn_attrs & JSPROP_LINT_IGNORE) == 0 && (pn->pn_attrs & JSPROP_LINT_DECLARED) == 0) {
545 if (!MarkIfDeclared(cx, ts, tc, pn))
546 return JS_FALSE;
547
548 if ((pn->pn_attrs & JSPROP_LINT_DECLARED) == 0) {
549 /* hack : temporarily change current line for error reporting */
550 JSBool res;
551 uintN start = CG_CURRENT_LINE(cg);
552 const char *name = js_AtomToPrintableString(cx, pn->pn_atom);
553
554 CG_CURRENT_LINE(cg) = pn->pn_pos.begin.lineno;
555 res = js_ReportCompileErrorNumber(cx, NULL, cg,
556 JSREPORT_WARNING |
557 JSREPORT_STRICT,
558 JSMSG_UNDECLARED_IDENTIFIER,
559 name);
560 CG_CURRENT_LINE(cg) = start;
561
562 if (!res)
563 return JS_FALSE;
564 }
565 }
566
567 switch (pn->pn_arity) {
568 case PN_LIST:
569 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
570 if (!WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn2, pn, parentNode)) {
571 return JS_FALSE;
572 }
573 }
574 return JS_TRUE;
575 case PN_TERNARY:
576 return WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_kid1, pn, parentNode) &&
577 WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_kid2, pn, parentNode) &&
578 WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_kid3, pn, parentNode);
579 case PN_BINARY:
580 return WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_left, pn, parentNode) &&
581 WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_right, pn, parentNode);
582 case PN_UNARY:
583 return WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_kid, pn, parentNode);
584 case PN_NAME:
585 return WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_expr, pn, parentNode);
586 case PN_FUNC:
587 return WarnUndeclaredIdentifiers(cg, cx, ts, tc, pn->pn_body, pn, parentNode);
588 default:
589 return JS_TRUE;
590 }
591 }
592
593 static JSBool
MarkDeclaredIdentifiers(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * pn,JSParseNode * parentNode,JSParseNode * grandParentNode)594 MarkDeclaredIdentifiers(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSParseNode *pn,
595 JSParseNode *parentNode, JSParseNode *grandParentNode)
596 {
597 JSParseNode *pn2;
598
599 if (!pn)
600 return JS_TRUE;
601
602 if (pn->pn_type == TOK_NAME) {
603 if (SHOULD_IGNORE_LINT_WARNINGS(cx)) {
604 /* nasty, but undeclared identifiers aren't warned against until end */
605 pn->pn_attrs |= JSPROP_LINT_IGNORE;
606 }
607 else if ((pn->pn_attrs & JSPROP_LINT_DECLARED) == 0) {
608 JSTreeContext *curtc;
609 JSAtomListElement *ale;
610
611 curtc = tc;
612 while (curtc) {
613 ATOM_LIST_SEARCH(ale, &curtc->decls, pn->pn_atom);
614 if (ale) {
615 pn->pn_attrs |= JSPROP_LINT_DECLARED;
616 break;
617 }
618 curtc = curtc->down;
619 }
620 }
621 }
622
623 switch (pn->pn_arity) {
624 case PN_LIST:
625 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
626 if (!MarkDeclaredIdentifiers(cx, ts, tc, pn2, pn, parentNode)) {
627 return JS_FALSE;
628 }
629 }
630 return JS_TRUE;
631 case PN_TERNARY:
632 return MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_kid1, pn, parentNode) &&
633 MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_kid2, pn, parentNode) &&
634 MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_kid3, pn, parentNode);
635 case PN_BINARY:
636 return MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_left, pn, parentNode) &&
637 MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_right, pn, parentNode);
638 case PN_UNARY:
639 return MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_kid, pn, parentNode);
640 case PN_NAME:
641 return MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_expr, pn, parentNode);
642 case PN_FUNC:
643 return MarkDeclaredIdentifiers(cx, ts, tc, pn->pn_body, pn, parentNode);
644 default:
645 return JS_TRUE;
646 }
647 }
648
649 /*
650 * Compile a top-level script.
651 */
652 JS_FRIEND_API(JSBool)
js_CompileTokenStream(JSContext * cx,JSObject * chain,JSTokenStream * ts,JSCodeGenerator * cg)653 js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
654 JSCodeGenerator *cg)
655 {
656 JSStackFrame *fp, frame;
657 uint32 flags;
658 JSParseNode *pn;
659 JSBool ok;
660 #ifdef METER_PARSENODES
661 void *sbrk(ptrdiff_t), *before = sbrk(0);
662 #endif
663
664 /*
665 * Push a compiler frame if we have no frames, or if the top frame is a
666 * lightweight function activation, or if its scope chain doesn't match
667 * the one passed to us.
668 */
669 fp = cx->fp;
670 if (!fp || !fp->varobj || fp->scopeChain != chain) {
671 memset(&frame, 0, sizeof frame);
672 frame.varobj = frame.scopeChain = chain;
673 if (cx->options & JSOPTION_VAROBJFIX) {
674 while ((chain = JS_GetParent(cx, chain)) != NULL)
675 frame.varobj = chain;
676 }
677 frame.down = fp;
678 cx->fp = &frame;
679 }
680 flags = cx->fp->flags;
681 cx->fp->flags = flags |
682 (JS_HAS_COMPILE_N_GO_OPTION(cx)
683 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
684 : JSFRAME_COMPILING);
685
686 /* Prevent GC activation while compiling. */
687 JS_KEEP_ATOMS(cx->runtime);
688
689 pn = Statements(cx, ts, &cg->treeContext);
690 if (!pn) {
691 ok = JS_FALSE;
692 } else if (!js_MatchToken(cx, ts, TOK_EOF)) {
693 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
694 JSMSG_SYNTAX_ERROR);
695 ok = JS_FALSE;
696 } else {
697 #ifdef METER_PARSENODES
698 printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",
699 (char *)sbrk(0) - (char *)before,
700 parsenodes,
701 maxparsenodes,
702 parsenodes - recyclednodes);
703 before = sbrk(0);
704 #endif
705
706 /*
707 * No need to emit code here -- Statements already has, for each
708 * statement in turn. Search for TCF_COMPILING in Statements, below.
709 * That flag is set for every tc == &cg->treeContext, and it implies
710 * that the tc can be downcast to a cg and used to emit code during
711 * parsing, rather than at the end of the parse phase.
712 */
713 JS_ASSERT(cg->treeContext.flags & TCF_COMPILING);
714 ok = JS_TRUE;
715 }
716
717 if (cx->lint) {
718 if (ok && cx->lint->controlCommentsIgnore &&
719 !js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING, JSMSG_MISMATCH_CTRL_COMMENTS)) {
720 ok = JS_FALSE;
721 }
722 /* forcibly turn off ignore so undeclared identifier warnings are displayed */
723 cx->lint->controlCommentsIgnore = JS_FALSE;
724
725 /*
726 * Two files may have import directives pointing to each other. All import directives
727 * must be processed after the entire script has been examined to allow such files to
728 * recognize each other's identifiers.
729 */
730 if (cx->lint->importPaths) {
731 while (!JS_CLIST_IS_EMPTY(&cx->lint->importPaths->links)) {
732 JSLImportPathList *curPath;
733 curPath = (JSLImportPathList*)JS_LIST_HEAD(&cx->lint->importPaths->links);
734 JS_REMOVE_LINK(&curPath->links);
735
736 if (cx->lint->importCallback)
737 cx->lint->importCallback(cx, curPath->importPath, cx->lint->importCallbackParms);
738
739 JS_free(cx, curPath->importPath);
740 JS_free(cx, curPath);
741 }
742 }
743
744 if (ok) {
745 if (cx->lint->alwaysUseOptionExplicit || cx->lint->controlCommentsOptionExplicit) {
746 /* warn against undeclared identifiers */
747 if (!WarnUndeclaredIdentifiers(cg, cx, ts, &cg->treeContext, pn, NULL, NULL))
748 ok = JS_FALSE;
749 }
750 else if (!cx->lint->hasCompletedPartialScript) {
751 /* warn about missing "option explicit" (but only once) */
752 if (!js_ReportCompileErrorNumber(cx, ts, cg, JSREPORT_WARNING, JSMSG_MISSING_OPTION_EXPLICIT))
753 ok = JS_FALSE;
754 }
755 }
756
757 cx->lint->hasCompletedPartialScript = JS_TRUE;
758 }
759
760 #ifdef METER_PARSENODES
761 printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
762 (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);
763 #endif
764 #ifdef JS_ARENAMETER
765 JS_DumpArenaStats(stdout);
766 #endif
767 JS_UNKEEP_ATOMS(cx->runtime);
768 cx->fp->flags = flags;
769 cx->fp = fp;
770 return ok;
771 }
772
773 /*
774 * Insist on a final return before control flows out of pn, but don't be too
775 * smart about loops (do {...; return e2;} while(0) at the end of a function
776 * that contains an early return e1 will get a strict-option-only warning).
777 */
778 #define ENDS_IN_OTHER 0
779 #define ENDS_IN_RETURN 1
780 #define ENDS_IN_BREAK 2
781
782 static int
HasFinalReturn(JSParseNode * pn,JSBool breakIsReturn)783 HasFinalReturn(JSParseNode *pn, JSBool breakIsReturn)
784 {
785 /* jsl: the break statement should be treated the same as a return for lint purposes (breakIsReturn) */
786 uintN rv, rv2, hasDefault;
787 JSParseNode *pn2, *pn3;
788
789 switch (pn->pn_type) {
790 case TOK_LC:
791 if (!pn->pn_head)
792 return ENDS_IN_OTHER;
793 return HasFinalReturn(PN_LAST(pn), breakIsReturn);
794
795 case TOK_IF:
796 rv = HasFinalReturn(pn->pn_kid2, breakIsReturn);
797 if (pn->pn_kid3)
798 rv &= HasFinalReturn(pn->pn_kid3, breakIsReturn);
799 return rv;
800
801 #if JS_HAS_SWITCH_STATEMENT
802 case TOK_SWITCH:
803 rv = ENDS_IN_RETURN;
804 hasDefault = ENDS_IN_OTHER;
805 for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) {
806 if (pn2->pn_type == TOK_DEFAULT)
807 hasDefault = ENDS_IN_RETURN;
808 pn3 = pn2->pn_right;
809 JS_ASSERT(pn3->pn_type == TOK_LC);
810 if (pn3->pn_head) {
811 rv2 = HasFinalReturn(PN_LAST(pn3), breakIsReturn);
812 if (rv2 == ENDS_IN_OTHER && pn2->pn_next)
813 /* Falling through to next case or default. */;
814 else
815 rv &= rv2;
816 }
817 }
818 /* If a final switch has no default case, we judge it harshly. */
819 rv &= hasDefault;
820 return rv;
821 #endif /* JS_HAS_SWITCH_STATEMENT */
822
823 case TOK_BREAK:
824 if (breakIsReturn)
825 return ENDS_IN_RETURN;
826 else
827 return ENDS_IN_BREAK;
828
829 case TOK_WITH:
830 return HasFinalReturn(pn->pn_right, breakIsReturn);
831
832 case TOK_RETURN:
833 return ENDS_IN_RETURN;
834
835 #if JS_HAS_EXCEPTIONS
836 case TOK_THROW:
837 return ENDS_IN_RETURN;
838
839 case TOK_TRY:
840 /* If we have a finally block that returns, we are done. */
841 if (pn->pn_kid3) {
842 rv = HasFinalReturn(pn->pn_kid3, breakIsReturn);
843 if (rv == ENDS_IN_RETURN)
844 return rv;
845 }
846
847 /* Else check the try block and any and all catch statements. */
848 rv = HasFinalReturn(pn->pn_kid1, breakIsReturn);
849 if (pn->pn_kid2)
850 rv &= HasFinalReturn(pn->pn_kid2, breakIsReturn);
851 return rv;
852
853 case TOK_CATCH:
854 /* Check this block's code and iterate over further catch blocks. */
855 rv = HasFinalReturn(pn->pn_kid3, breakIsReturn);
856 for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
857 rv &= HasFinalReturn(pn2->pn_kid3, breakIsReturn);
858 return rv;
859 #endif
860
861 default:
862 return ENDS_IN_OTHER;
863 }
864 }
865
866 static JSBool
ReportNoReturnValue(JSContext * cx,JSTokenStream * ts)867 ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
868 {
869 JSFunction *fun;
870 JSBool ok;
871
872 fun = cx->fp->fun;
873 if (fun->atom) {
874 char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));
875 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
876 JSREPORT_WARNING |
877 JSREPORT_STRICT,
878 JSMSG_NO_RETURN_VALUE, name);
879 } else {
880 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
881 JSREPORT_WARNING |
882 JSREPORT_STRICT,
883 JSMSG_ANON_NO_RETURN_VALUE);
884 }
885 return ok;
886 }
887
888 static JSBool
CheckFinalReturn(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)889 CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
890 {
891 return HasFinalReturn(pn, JS_FALSE) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
892 }
893
894 static JSParseNode *
FunctionBody(JSContext * cx,JSTokenStream * ts,JSFunction * fun,JSTreeContext * tc)895 FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,
896 JSTreeContext *tc)
897 {
898 JSStackFrame *fp, frame;
899 JSObject *funobj;
900 uintN oldflags;
901 JSParseNode *pn;
902
903 fp = cx->fp;
904 funobj = fun->object;
905 if (!fp || fp->fun != fun || fp->varobj != funobj ||
906 fp->scopeChain != funobj) {
907 memset(&frame, 0, sizeof frame);
908 frame.fun = fun;
909 frame.varobj = frame.scopeChain = funobj;
910 frame.down = fp;
911 frame.flags = (fp->flags & JSFRAME_COMPILE_N_GO);
912 cx->fp = &frame;
913 }
914
915 oldflags = tc->flags;
916 tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
917 tc->flags |= TCF_IN_FUNCTION;
918 pn = Statements(cx, ts, tc);
919
920 /* Check for falling off the end of a function that returns a value. */
921 if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {
922 if (!CheckFinalReturn(cx, ts, pn))
923 pn = NULL;
924 }
925
926 cx->fp = fp;
927 /* pass thru return value information for lint */
928 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS) | (cx->lint ? (tc->flags & TCF_RETURN_EXPR) : 0);
929 return pn;
930 }
931
932 /*
933 * Compile a JS function body, which might appear as the value of an event
934 * handler attribute in an HTML <INPUT> tag.
935 */
936 JSBool
js_CompileFunctionBody(JSContext * cx,JSTokenStream * ts,JSFunction * fun)937 js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
938 {
939 JSArenaPool codePool, notePool;
940 JSCodeGenerator funcg;
941 JSStackFrame *fp, frame;
942 JSObject *funobj;
943 JSParseNode *pn;
944 JSBool ok;
945
946 JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
947 JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote));
948 if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool,
949 ts->filename, ts->lineno,
950 ts->principals)) {
951 return JS_FALSE;
952 }
953
954 /* Prevent GC activation while compiling. */
955 JS_KEEP_ATOMS(cx->runtime);
956
957 /* Push a JSStackFrame for use by FunctionBody. */
958 fp = cx->fp;
959 funobj = fun->object;
960 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
961 fp->scopeChain != funobj));
962 memset(&frame, 0, sizeof frame);
963 frame.fun = fun;
964 frame.varobj = frame.scopeChain = funobj;
965 frame.down = fp;
966 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
967 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
968 : JSFRAME_COMPILING;
969 cx->fp = &frame;
970
971 /* Ensure that the body looks like a block statement to js_EmitTree. */
972 CURRENT_TOKEN(ts).type = TOK_LC;
973 pn = FunctionBody(cx, ts, fun, &funcg.treeContext);
974 if (!pn) {
975 ok = JS_FALSE;
976 } else {
977 /*
978 * No need to emit code here -- Statements (via FunctionBody) already
979 * has. See similar comment in js_CompileTokenStream, and bug 108257.
980 */
981 fun->u.script = js_NewScriptFromCG(cx, &funcg, fun);
982 if (!fun->u.script) {
983 ok = JS_FALSE;
984 } else {
985 fun->interpreted = JS_TRUE;
986 if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT)
987 fun->flags |= JSFUN_HEAVYWEIGHT;
988 ok = JS_TRUE;
989 }
990 }
991
992 /* Restore saved state and release code generation arenas. */
993 cx->fp = fp;
994 JS_UNKEEP_ATOMS(cx->runtime);
995 js_FinishCodeGenerator(cx, &funcg);
996 JS_FinishArenaPool(&codePool);
997 JS_FinishArenaPool(¬ePool);
998 return ok;
999 }
1000
1001 static JSParseNode *
FunctionDef(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool lambda)1002 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
1003 JSBool lambda)
1004 {
1005 JSOp op, prevop;
1006 JSParseNode *pn, *body;
1007 JSAtom *funAtom, *argAtom;
1008 JSStackFrame *fp;
1009 JSObject *varobj, *pobj;
1010 JSAtomListElement *ale;
1011 JSProperty *prop;
1012 JSFunction *fun;
1013 uintN dupflag;
1014 JSBool ok;
1015 JSTreeContext funtc;
1016
1017 /* Make a TOK_FUNCTION node. */
1018 #if JS_HAS_GETTER_SETTER
1019 op = CURRENT_TOKEN(ts).t_op;
1020 #endif
1021 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc);
1022 if (!pn)
1023 return NULL;
1024
1025 /* Scan the optional function name into funAtom. */
1026 funAtom = js_MatchToken(cx, ts, TOK_NAME) ? CURRENT_TOKEN(ts).t_atom : NULL;
1027
1028 if (cx->lint && cx->lint->enableJScriptFunctionExtensions) {
1029 /* match multiple dot sequences */
1030 while (js_MatchToken(cx, ts, TOK_DOT)) {
1031 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_SYNTAX_ERROR);
1032 funAtom = NULL;
1033 }
1034
1035 /* match double-colon syntax */
1036 if (js_MatchToken(cx, ts, TOK_COLON)) {
1037 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_SYNTAX_ERROR);
1038 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_SYNTAX_ERROR);
1039 funAtom = NULL;
1040 }
1041 }
1042
1043 /* Find the nearest variable-declaring scope and use it as our parent. */
1044 fp = cx->fp;
1045 varobj = fp->varobj;
1046
1047 /*
1048 * Record names for function statements in tc->decls so we know when to
1049 * avoid optimizing variable references that might name a function.
1050 */
1051 if (!lambda && funAtom) {
1052 ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
1053 if (ale) {
1054 prevop = ALE_JSOP(ale);
1055 if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
1056 const char *name = js_AtomToPrintableString(cx, funAtom);
1057 if (!name ||
1058 !js_ReportCompileErrorNumber(cx, ts, NULL,
1059 (prevop != JSOP_DEFCONST)
1060 ? JSREPORT_WARNING |
1061 JSREPORT_STRICT
1062 : JSREPORT_ERROR,
1063 JSMSG_REDECLARED_VAR,
1064 (prevop == JSOP_DEFFUN ||
1065 prevop == JSOP_CLOSURE)
1066 ? js_function_str
1067 : (prevop == JSOP_DEFCONST)
1068 ? js_const_str
1069 : js_var_str,
1070 name)) {
1071 return NULL;
1072 }
1073 }
1074 if (tc->topStmt && prevop == JSOP_DEFVAR)
1075 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
1076 } else {
1077 /* save global identifier for lint */
1078 if (cx->lint && cx->lint->scriptIdentifiers && !tc->down) {
1079 jsval val;
1080 val = INT_TO_JSVAL(0);
1081 if (!js_SetProperty(cx, cx->lint->scriptIdentifiers, (jsid)funAtom, &val))
1082 return NULL;
1083 }
1084 ale = js_IndexAtom(cx, funAtom, &tc->decls);
1085 if (!ale)
1086 return NULL;
1087 }
1088 ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
1089
1090 #if JS_HAS_LEXICAL_CLOSURE
1091 /*
1092 * A function nested at top level inside another's body needs only a
1093 * local variable to bind its name to its value, and not an activation
1094 * object property (it might also need the activation property, if the
1095 * outer function contains with statements, e.g., but the stack slot
1096 * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
1097 * JSOP_GETVAR bytecode).
1098 */
1099 if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
1100 /*
1101 * Define a property on the outer function so that LookupArgOrVar
1102 * can properly optimize accesses.
1103 *
1104 * XXX Here and in Variables, we use the function object's scope,
1105 * XXX arguably polluting it, when we could use a compiler-private
1106 * XXX scope structure. Tradition!
1107 */
1108 JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
1109 JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
1110 if (!js_LookupProperty(cx, varobj, (jsid)funAtom, &pobj, &prop))
1111 return NULL;
1112 if (prop)
1113 OBJ_DROP_PROPERTY(cx, pobj, prop);
1114 if (!prop || pobj != varobj) {
1115 if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom,
1116 JSVAL_VOID,
1117 js_GetLocalVariable,
1118 js_SetLocalVariable,
1119 JSPROP_ENUMERATE | JSPROP_SHARED,
1120 SPROP_HAS_SHORTID, fp->fun->nvars,
1121 NULL)) {
1122 return NULL;
1123 }
1124 fp->fun->nvars++;
1125 }
1126 }
1127 #endif
1128 }
1129
1130 fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj,
1131 funAtom);
1132 if (!fun)
1133 return NULL;
1134 #if JS_HAS_GETTER_SETTER
1135 if (op != JSOP_NOP)
1136 fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;
1137 #endif
1138
1139 /* Now parse formal argument list and compute fun->nargs. */
1140 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
1141 if (!js_MatchToken(cx, ts, TOK_RP)) {
1142 do {
1143 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);
1144 argAtom = CURRENT_TOKEN(ts).t_atom;
1145 pobj = NULL;
1146 if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,
1147 &prop)) {
1148 return NULL;
1149 }
1150 dupflag = 0;
1151 if (prop) {
1152 ok = JS_TRUE;
1153 if (pobj == fun->object &&
1154 ((JSScopeProperty *) prop)->getter == js_GetArgument) {
1155 const char *name = js_AtomToPrintableString(cx, argAtom);
1156
1157 /*
1158 * A duplicate parameter name. We force a duplicate node
1159 * on the SCOPE_LAST_PROP(scope) list with the same id,
1160 * distinguished by the SPROP_IS_DUPLICATE flag, and not
1161 * mapped by an entry in scope.
1162 */
1163 ok = name &&
1164 js_ReportCompileErrorNumber(cx, ts, NULL,
1165 JSREPORT_WARNING |
1166 JSREPORT_STRICT,
1167 JSMSG_DUPLICATE_FORMAL,
1168 name);
1169
1170 dupflag = SPROP_IS_DUPLICATE;
1171 }
1172 OBJ_DROP_PROPERTY(cx, pobj, prop);
1173 if (!ok)
1174 return NULL;
1175 prop = NULL;
1176 }
1177 if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom,
1178 js_GetArgument, js_SetArgument,
1179 SPROP_INVALID_SLOT,
1180 JSPROP_ENUMERATE | JSPROP_PERMANENT |
1181 JSPROP_SHARED,
1182 SPROP_HAS_SHORTID | dupflag,
1183 fun->nargs)) {
1184 return NULL;
1185 }
1186 fun->nargs++;
1187 } while (js_MatchToken(cx, ts, TOK_COMMA));
1188
1189 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
1190 }
1191
1192 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
1193 pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
1194
1195 TREE_CONTEXT_INIT(&funtc);
1196 funtc.down = tc;
1197 body = FunctionBody(cx, ts, fun, &funtc);
1198 if (!body)
1199 return NULL;
1200
1201 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
1202 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1203
1204 #if JS_HAS_LEXICAL_CLOSURE
1205 /*
1206 * If we collected flags that indicate nested heavyweight functions, or
1207 * this function contains heavyweight-making statements (references to
1208 * __parent__ or __proto__; use of with, eval, import, or export; and
1209 * assignment to arguments), flag the function as heavyweight (requiring
1210 * a call object per invocation).
1211 */
1212 if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
1213 fun->flags |= JSFUN_HEAVYWEIGHT;
1214 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1215 } else {
1216 /*
1217 * If this function is a named statement function not at top-level
1218 * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
1219 * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
1220 * enclosing function, if any, must be heavyweight.
1221 */
1222 if ((!lambda && funAtom && tc->topStmt) ||
1223 (funtc.flags & TCF_FUN_USES_NONLOCALS)) {
1224 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1225 }
1226 }
1227 #endif
1228
1229 #if JS_HAS_LEXICAL_CLOSURE
1230 if (lambda || !funAtom) {
1231 /*
1232 * ECMA ed. 3 standard: function expression, possibly anonymous (even
1233 * if at top-level, an unnamed function is an expression statement, not
1234 * a function declaration).
1235 */
1236 op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
1237 } else if (tc->topStmt) {
1238 /*
1239 * ECMA ed. 3 extension: a function expression statement not at the
1240 * top level, e.g., in a compound statement such as the "then" part
1241 * of an "if" statement, binds a closure only if control reaches that
1242 * sub-statement.
1243 */
1244 op = JSOP_CLOSURE;
1245 } else
1246 #endif
1247 op = JSOP_NOP;
1248
1249 /*
1250 * Absent use of the new scoped local GC roots API around compiler calls,
1251 * we need to atomize here to protect against a GC activation. Atoms are
1252 * protected from GC during compilation by the JS_FRIEND_API entry points
1253 * in this file. There doesn't seem to be any gain in switching from the
1254 * atom-keeping method to the bulkier, slower scoped local roots method.
1255 */
1256 pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
1257 if (!pn->pn_funAtom)
1258 return NULL;
1259
1260 pn->pn_op = op;
1261 pn->pn_body = body;
1262 pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;
1263 pn->pn_tryCount = funtc.tryCount;
1264 TREE_CONTEXT_FINISH(&funtc);
1265
1266 /* Flag declared identifiers before this goes out of scope */
1267 if (cx->lint)
1268 MarkDeclaredIdentifiers(cx, ts, &funtc, pn, NULL, NULL);
1269
1270 return pn;
1271 }
1272
1273 static JSParseNode *
FunctionStmt(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1274 FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1275 {
1276 return FunctionDef(cx, ts, tc, JS_FALSE);
1277 }
1278
1279 #if JS_HAS_LEXICAL_CLOSURE
1280 static JSParseNode *
FunctionExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1281 FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1282 {
1283 return FunctionDef(cx, ts, tc, JS_TRUE);
1284 }
1285 #endif
1286
1287 static JSBool
isIncDec(JSParseNode * pn)1288 isIncDec(JSParseNode *pn)
1289 {
1290 return pn && pn->pn_arity == PN_UNARY &&
1291 (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC);
1292 }
1293
1294 static JSBool
hasIncDec(JSParseNode * pn)1295 hasIncDec(JSParseNode *pn)
1296 {
1297 JSParseNode *pn2;
1298
1299 if (!pn)
1300 return JS_FALSE;
1301
1302 if (isIncDec(pn))
1303 return JS_TRUE;
1304
1305 switch (pn->pn_arity) {
1306 case PN_LIST:
1307 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1308 if (pn->pn_type == TOK_COMMA && isIncDec(pn2))
1309 continue;
1310 if (hasIncDec(pn2))
1311 return JS_TRUE;
1312 }
1313 return JS_FALSE;
1314 case PN_TERNARY:
1315 return hasIncDec(pn->pn_kid1) ||
1316 hasIncDec(pn->pn_kid2) ||
1317 hasIncDec(pn->pn_kid3);
1318 case PN_BINARY:
1319 return hasIncDec(pn->pn_left) || hasIncDec(pn->pn_right);
1320 case PN_UNARY:
1321 return hasIncDec(pn->pn_kid);
1322 case PN_NAME:
1323 return hasIncDec(pn->pn_expr);
1324 default:
1325 return JS_FALSE;
1326 }
1327 }
1328
1329 static JSBool
warnIfHasIncDec(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)1330 warnIfHasIncDec(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1331 {
1332 if (!hasIncDec(pn))
1333 return JS_TRUE;
1334
1335 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1336 JSREPORT_WARNING |
1337 JSREPORT_STRICT,
1338 JSMSG_INC_DEC_WITHIN_STMT)) {
1339 return JS_FALSE;
1340 }
1341
1342 return JS_TRUE;
1343 }
1344
1345 /*
1346 * Parse the statements in a block, creating a TOK_LC node that lists the
1347 * statements' trees. If called from block-parsing code, the caller must
1348 * match { before and } after.
1349 */
1350 static JSParseNode *
Statements(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1351 Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1352 {
1353 JSParseNode *pn, *pn2;
1354 JSTokenType tt;
1355 JSBool hadtoken, foundPass;
1356 JSBool beyondreach, alreadywarnedunreachable;
1357
1358 CHECK_RECURSION();
1359
1360 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1361 if (!pn)
1362 return NULL;
1363 PN_INIT_LIST(pn);
1364
1365 ts->flags |= TSF_OPERAND;
1366 hadtoken = JS_FALSE;
1367 foundPass = JS_FALSE;
1368 beyondreach = JS_FALSE;
1369 alreadywarnedunreachable = JS_FALSE;
1370
1371 /* Check for the "pass" keyword when the next token is scanned.
1372 */
1373 if (cx->lint && tc->topStmt) {
1374 cx->lint->controlCommentsAllowPass = JS_TRUE;
1375 cx->lint->controlCommentsFoundPass = JS_FALSE;
1376 tt = js_PeekToken(cx, ts);
1377 foundPass = cx->lint->controlCommentsFoundPass;
1378 cx->lint->controlCommentsAllowPass = JS_FALSE;
1379 cx->lint->controlCommentsFoundPass = JS_FALSE;
1380 }
1381
1382 while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
1383 if (beyondreach && !alreadywarnedunreachable) {
1384 alreadywarnedunreachable = JS_TRUE;
1385 if (cx->lint && !js_ReportCompileErrorNumber(cx, ts, NULL,
1386 JSREPORT_WARNING |
1387 JSREPORT_STRICT,
1388 JSMSG_UNREACHABLE_CODE)) {
1389 return NULL;
1390 }
1391 }
1392
1393 if (foundPass && !js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING, JSMSG_INVALID_PASS))
1394 return NULL;
1395
1396 hadtoken = JS_TRUE;
1397 beyondreach = (beyondreach || tt == TOK_BREAK || tt == TOK_CONTINUE || tt == TOK_RETURN || tt == TOK_THROW);
1398 ts->flags &= ~TSF_OPERAND;
1399 pn2 = Statement(cx, ts, tc);
1400 if (!pn2)
1401 return NULL;
1402
1403 ts->flags |= TSF_OPERAND;
1404
1405 /*
1406 * If compiling top-level statements, emit as we go to save space,
1407 * except when linting (so we can check for undeclared identifiers)
1408 */
1409 if (!tc->topStmt && (tc->flags & TCF_COMPILING) && !cx->lint) {
1410 if (cx->fp->fun &&
1411 JS_HAS_STRICT_OPTION(cx) &&
1412 (tc->flags & TCF_RETURN_EXPR)) {
1413 /*
1414 * Check pn2 for lack of a final return statement if it is the
1415 * last statement in the block.
1416 */
1417 tt = js_PeekToken(cx, ts);
1418 if ((tt == TOK_EOF || tt == TOK_RC) &&
1419 !CheckFinalReturn(cx, ts, pn2)) {
1420 tt = TOK_ERROR;
1421 break;
1422 }
1423
1424 /*
1425 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
1426 * CheckFinalReturn again.
1427 */
1428 tc->flags &= ~TCF_RETURN_EXPR;
1429 }
1430 if (!js_FoldConstants(cx, pn2, tc) ||
1431 !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
1432 !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
1433 tt = TOK_ERROR;
1434 break;
1435 }
1436 RecycleTree(pn2, tc);
1437 } else {
1438 PN_APPEND(pn, pn2);
1439 }
1440 }
1441 ts->flags &= ~TSF_OPERAND;
1442 if (tt == TOK_ERROR)
1443 return NULL;
1444
1445 /* empty catch is useful */
1446 if (cx->lint && !hadtoken && !foundPass && tc->topStmt && tc->topStmt->type != STMT_CATCH) {
1447 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1448 JSREPORT_WARNING | JSREPORT_STRICT,
1449 JSMSG_EMPTY_STATEMENT)) {
1450 return NULL;
1451 }
1452 }
1453
1454 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1455 return pn;
1456 }
1457
1458 static JSParseNode *
Condition(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1459 Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1460 {
1461 JSParseNode *pn, *pn2;
1462
1463 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
1464 pn = Expr(cx, ts, tc);
1465 if (!pn)
1466 return NULL;
1467 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
1468
1469 /*
1470 * Check for (a = b) and "correct" it to (a == b) iff b's operator has
1471 * greater precedence than ==.
1472 * XXX not ECMA, but documented in several books -- now a strict warning.
1473 */
1474 if (pn->pn_type == TOK_ASSIGN &&
1475 pn->pn_op == JSOP_NOP &&
1476 pn->pn_right->pn_type > TOK_EQOP)
1477 {
1478 JSBool rewrite = !JSVERSION_IS_ECMA(cx->version);
1479 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1480 JSREPORT_WARNING | JSREPORT_STRICT,
1481 JSMSG_EQUAL_AS_ASSIGN,
1482 rewrite
1483 ? "\nAssuming equality test"
1484 : "")) {
1485 return NULL;
1486 }
1487 if (rewrite) {
1488 pn->pn_type = TOK_EQOP;
1489 pn->pn_op = (JSOp)cx->jsop_eq;
1490 pn2 = pn->pn_left;
1491 switch (pn2->pn_op) {
1492 case JSOP_SETNAME:
1493 pn2->pn_op = JSOP_NAME;
1494 break;
1495 case JSOP_SETPROP:
1496 pn2->pn_op = JSOP_GETPROP;
1497 break;
1498 case JSOP_SETELEM:
1499 pn2->pn_op = JSOP_GETELEM;
1500 break;
1501 default:
1502 JS_ASSERT(0);
1503 }
1504 }
1505 }
1506 return pn;
1507 }
1508
1509 static JSBool
MatchLabel(JSContext * cx,JSTokenStream * ts,JSParseNode * pn)1510 MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
1511 {
1512 JSAtom *label;
1513 #if JS_HAS_LABEL_STATEMENT
1514 JSTokenType tt;
1515
1516 tt = js_PeekTokenSameLine(cx, ts);
1517 if (tt == TOK_ERROR)
1518 return JS_FALSE;
1519 if (tt == TOK_NAME) {
1520 (void) js_GetToken(cx, ts);
1521 label = CURRENT_TOKEN(ts).t_atom;
1522 } else {
1523 label = NULL;
1524 }
1525 #else
1526 label = NULL;
1527 #endif
1528 pn->pn_atom = label;
1529 return JS_TRUE;
1530 }
1531
1532 #if JS_HAS_EXPORT_IMPORT
1533 static JSParseNode *
ImportExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1534 ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1535 {
1536 JSParseNode *pn, *pn2, *pn3;
1537 JSTokenType tt;
1538
1539 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
1540 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1541 if (!pn)
1542 return NULL;
1543 pn->pn_op = JSOP_NAME;
1544 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
1545 pn->pn_expr = NULL;
1546 pn->pn_slot = -1;
1547 pn->pn_attrs = 0;
1548
1549 ts->flags |= TSF_OPERAND;
1550 while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
1551 ts->flags &= ~TSF_OPERAND;
1552 if (pn->pn_op == JSOP_IMPORTALL)
1553 goto bad_import;
1554
1555 if (tt == TOK_DOT) {
1556 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1557 if (!pn2)
1558 return NULL;
1559 if (js_MatchToken(cx, ts, TOK_STAR)) {
1560 pn2->pn_op = JSOP_IMPORTALL;
1561 pn2->pn_atom = NULL;
1562 } else {
1563 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
1564 pn2->pn_op = JSOP_GETPROP;
1565 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1566 pn2->pn_slot = -1;
1567 pn2->pn_attrs = 0;
1568 }
1569 pn2->pn_expr = pn;
1570 pn2->pn_pos.begin = pn->pn_pos.begin;
1571 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1572 } else {
1573 /* Make a TOK_LB node. */
1574 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1575 if (!pn2)
1576 return NULL;
1577 pn3 = Expr(cx, ts, tc);
1578 if (!pn3)
1579 return NULL;
1580
1581 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
1582 pn2->pn_pos.begin = pn->pn_pos.begin;
1583 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
1584
1585 pn2->pn_op = JSOP_GETELEM;
1586 pn2->pn_left = pn;
1587 pn2->pn_right = pn3;
1588 }
1589
1590 pn = pn2;
1591 ts->flags |= TSF_OPERAND;
1592 }
1593 ts->flags &= ~TSF_OPERAND;
1594 if (tt == TOK_ERROR)
1595 return NULL;
1596 js_UngetToken(ts);
1597
1598 switch (pn->pn_op) {
1599 case JSOP_GETPROP:
1600 pn->pn_op = JSOP_IMPORTPROP;
1601 break;
1602 case JSOP_GETELEM:
1603 pn->pn_op = JSOP_IMPORTELEM;
1604 break;
1605 case JSOP_IMPORTALL:
1606 break;
1607 default:
1608 goto bad_import;
1609 }
1610 return pn;
1611
1612 bad_import:
1613 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT);
1614 return NULL;
1615 }
1616 #endif /* JS_HAS_EXPORT_IMPORT */
1617
1618 extern const char js_with_statement_str[];
1619
1620 static JSBool
AreExpressionsIdentical(JSContext * cx,JSParseNode * pn1,JSParseNode * pn2,JSBool functionsAreIdentical)1621 AreExpressionsIdentical(JSContext *cx, JSParseNode *pn1, JSParseNode *pn2, JSBool functionsAreIdentical)
1622 {
1623 JSParseNode *child1, *child2;
1624
1625 if (!pn1 && !pn2)
1626 return JS_TRUE;
1627 else if (!pn1 || !pn2)
1628 return JS_FALSE;
1629
1630 /* check types */
1631 if (pn1->pn_type != pn2->pn_type)
1632 return JS_FALSE;
1633 if (pn1->pn_op != pn2->pn_op)
1634 return JS_FALSE;
1635 if (pn1->pn_arity != pn2->pn_arity)
1636 return JS_FALSE;
1637
1638 /* bail out with function */
1639 if (!functionsAreIdentical) {
1640 if (pn1->pn_type == TOK_FUNCTION)
1641 return JS_FALSE;
1642 else if (pn1->pn_type == TOK_LP && pn1->pn_op == JSOP_CALL)
1643 return JS_FALSE;
1644 }
1645
1646 /* check atoms on names, properties, and string constants */
1647 if (pn1->pn_type == TOK_NAME ||
1648 pn1->pn_type == TOK_DOT ||
1649 pn1->pn_type == TOK_STRING) {
1650 if (pn1->pn_atom != pn2->pn_atom)
1651 return JS_FALSE;
1652 }
1653
1654 /* check values on numbers */
1655 if (pn1->pn_type == TOK_NUMBER)
1656 {
1657 if (pn1->pn_dval != pn2->pn_dval)
1658 return JS_FALSE;
1659 }
1660
1661 switch (pn1->pn_arity) {
1662 case PN_LIST:
1663 if (pn1->pn_count != pn2->pn_count)
1664 return JS_FALSE;
1665 for (child1 = pn1->pn_head, child2 = pn2->pn_head; child1 && child2;
1666 child1 = child1->pn_next, child2 = child2->pn_next) {
1667
1668 if (!AreExpressionsIdentical(cx, child1, child2, functionsAreIdentical)) {
1669 return JS_FALSE;
1670 }
1671 }
1672 return JS_TRUE;
1673 case PN_TERNARY:
1674 return AreExpressionsIdentical(cx, pn1->pn_kid1, pn2->pn_kid1, functionsAreIdentical) &&
1675 AreExpressionsIdentical(cx, pn1->pn_kid2, pn2->pn_kid2, functionsAreIdentical) &&
1676 AreExpressionsIdentical(cx, pn1->pn_kid3, pn2->pn_kid3, functionsAreIdentical);
1677 case PN_BINARY:
1678 return AreExpressionsIdentical(cx, pn1->pn_left, pn2->pn_left, functionsAreIdentical) &&
1679 AreExpressionsIdentical(cx, pn1->pn_right, pn2->pn_right, functionsAreIdentical);
1680 case PN_UNARY:
1681 return AreExpressionsIdentical(cx, pn1->pn_kid, pn2->pn_kid, functionsAreIdentical);
1682 case PN_NAME:
1683 return AreExpressionsIdentical(cx, pn1->pn_expr, pn2->pn_expr, functionsAreIdentical);
1684 case PN_FUNC:
1685 return AreExpressionsIdentical(cx, pn1->pn_body, pn2->pn_body, functionsAreIdentical);
1686 default:
1687 return JS_TRUE;
1688 }
1689 }
1690
1691 static JSParseNode *
Statement(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)1692 Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
1693 {
1694 JSTokenType tt;
1695 JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;
1696 JSStmtInfo stmtInfo, *stmt, *stmt2;
1697 JSAtom *label;
1698
1699 CHECK_RECURSION();
1700
1701 if (cx->lint && tc->topStmt && tc->topStmt->type == STMT_ELSE) {
1702 /*
1703 * check for an else statement that could be matched against multiple if statements
1704 * (go up the statement stack looking for an if statement until a block statement is hit)
1705 */
1706 JSStmtInfo *curStmt = tc->topStmt->down;
1707 while (curStmt && curStmt->type != STMT_BLOCK && curStmt->type != STMT_TRY &&
1708 curStmt->type != STMT_CATCH && curStmt->type != STMT_FINALLY) {
1709 if (curStmt->type == STMT_IF) {
1710 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1711 JSREPORT_WARNING | JSREPORT_STRICT,
1712 JSMSG_AMBIGUOUS_ELSE_STMT)) {
1713 return NULL;
1714 }
1715 break;
1716 }
1717 curStmt = curStmt->down;
1718 }
1719 }
1720
1721 ts->flags |= TSF_OPERAND;
1722 tt = js_GetToken(cx, ts);
1723 ts->flags &= ~TSF_OPERAND;
1724
1725 if (cx->lint && tt != TOK_LC && tc->topStmt &&
1726 !(tc->topStmt->type == STMT_ELSE && tt == TOK_IF)/* allow else if statements */) {
1727 switch (tc->topStmt->type) {
1728 case STMT_BLOCK:
1729 case STMT_LABEL:
1730 case STMT_SWITCH:
1731 case STMT_SUBROUTINE:
1732 /* ignore */
1733 break;
1734
1735 case STMT_TRY:
1736 case STMT_CATCH:
1737 case STMT_FINALLY:
1738 /* syntax always requires curly */
1739 break;
1740
1741 case STMT_IF:
1742 case STMT_ELSE:
1743 case STMT_WITH:
1744 case STMT_DO_LOOP:
1745 case STMT_FOR_LOOP:
1746 case STMT_FOR_IN_LOOP:
1747 case STMT_WHILE_LOOP:
1748 if (tt == TOK_IF || tt == TOK_WHILE || tt == TOK_DO || tt == TOK_FOR || tt == TOK_WITH) {
1749 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1750 JSREPORT_WARNING |
1751 JSREPORT_STRICT,
1752 JSMSG_AMBIGUOUS_NESTED_STMT)) {
1753 return NULL;
1754 }
1755 }
1756 else if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1757 JSREPORT_WARNING |
1758 JSREPORT_STRICT,
1759 JSMSG_BLOCK_WITHOUT_BRACES)) {
1760 return NULL;
1761 }
1762 }
1763 }
1764
1765 #if JS_HAS_GETTER_SETTER
1766 if (tt == TOK_NAME) {
1767 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
1768 if (tt == TOK_ERROR)
1769 return NULL;
1770 }
1771 #endif
1772
1773 switch (tt) {
1774 #if JS_HAS_EXPORT_IMPORT
1775 case TOK_EXPORT:
1776 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1777 if (!pn)
1778 return NULL;
1779 PN_INIT_LIST(pn);
1780 if (js_MatchToken(cx, ts, TOK_STAR)) {
1781 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
1782 if (!pn2)
1783 return NULL;
1784 PN_APPEND(pn, pn2);
1785 } else {
1786 do {
1787 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);
1788 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
1789 if (!pn2)
1790 return NULL;
1791 pn2->pn_op = JSOP_NAME;
1792 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
1793 pn2->pn_expr = NULL;
1794 pn2->pn_slot = -1;
1795 pn2->pn_attrs = 0;
1796 PN_APPEND(pn, pn2);
1797 } while (js_MatchToken(cx, ts, TOK_COMMA));
1798 }
1799 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1800 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1801 break;
1802
1803 case TOK_IMPORT:
1804 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1805 if (!pn)
1806 return NULL;
1807 PN_INIT_LIST(pn);
1808 do {
1809 pn2 = ImportExpr(cx, ts, tc);
1810 if (!pn2)
1811 return NULL;
1812 PN_APPEND(pn, pn2);
1813 } while (js_MatchToken(cx, ts, TOK_COMMA));
1814 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
1815 tc->flags |= TCF_FUN_HEAVYWEIGHT;
1816 break;
1817 #endif /* JS_HAS_EXPORT_IMPORT */
1818
1819 case TOK_FUNCTION:
1820 return FunctionStmt(cx, ts, tc);
1821
1822 case TOK_IF:
1823 /* An IF node has three kids: condition, then, and optional else. */
1824 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
1825 if (!pn)
1826 return NULL;
1827 pn1 = Condition(cx, ts, tc);
1828 if (!pn1)
1829 return NULL;
1830 js_PushStatement(tc, &stmtInfo, STMT_IF, -1);
1831 pn2 = Statement(cx, ts, tc);
1832 if (!pn2)
1833 return NULL;
1834 if (js_MatchToken(cx, ts, TOK_ELSE)) {
1835 stmtInfo.type = STMT_ELSE;
1836 pn3 = Statement(cx, ts, tc);
1837 if (!pn3)
1838 return NULL;
1839 pn->pn_pos.end = pn3->pn_pos.end;
1840 } else {
1841 pn3 = NULL;
1842 pn->pn_pos.end = pn2->pn_pos.end;
1843 }
1844 js_PopStatement(tc);
1845 pn->pn_kid1 = pn1;
1846 pn->pn_kid2 = pn2;
1847 pn->pn_kid3 = pn3;
1848 return pn;
1849
1850 #if JS_HAS_SWITCH_STATEMENT
1851 case TOK_SWITCH:
1852 {
1853 JSParseNode *pn5;
1854 JSBool seenDefault = JS_FALSE;
1855
1856 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1857 if (!pn)
1858 return NULL;
1859 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);
1860
1861 /* pn1 points to the switch's discriminant. */
1862 pn1 = Expr(cx, ts, tc);
1863 if (!pn1)
1864 return NULL;
1865
1866 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);
1867 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);
1868
1869 /* pn2 is a list of case nodes. The default case has pn_left == NULL */
1870 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1871 if (!pn2)
1872 return NULL;
1873 PN_INIT_LIST(pn2);
1874
1875 js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);
1876
1877 while ((tt = js_GetToken(cx, ts)) != TOK_RC) {
1878 switch (tt) {
1879 case TOK_DEFAULT:
1880 if (seenDefault) {
1881 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1882 JSMSG_TOO_MANY_DEFAULTS);
1883 return NULL;
1884 }
1885 seenDefault = JS_TRUE;
1886 /* fall through */
1887
1888 case TOK_CASE:
1889 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
1890 if (!pn3)
1891 return NULL;
1892 if (tt == TOK_DEFAULT) {
1893 pn3->pn_left = NULL;
1894 } else {
1895 pn3->pn_left = Expr(cx, ts, tc);
1896 if (!pn3->pn_left)
1897 return NULL;
1898
1899 /* check for duplicate case statements */
1900 for (pn5 = pn2->pn_head; pn5; pn5 = pn5->pn_next) {
1901 /* assume that function calls are identical */
1902 if (AreExpressionsIdentical(cx, pn5->pn_left, pn3->pn_left, JS_TRUE)) {
1903 if (!js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT,
1904 JSMSG_DUPLICATE_CASE_IN_SWITCH)) {
1905 return NULL;
1906 }
1907 break;
1908 }
1909 }
1910 }
1911 PN_APPEND(pn2, pn3);
1912 if (pn2->pn_count == JS_BIT(16)) {
1913 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1914 JSMSG_TOO_MANY_CASES);
1915 return NULL;
1916 }
1917 break;
1918
1919 case TOK_ERROR:
1920 return NULL;
1921
1922 default:
1923 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
1924 JSMSG_BAD_SWITCH);
1925 return NULL;
1926 }
1927 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
1928
1929 pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
1930 if (!pn4)
1931 return NULL;
1932 pn4->pn_type = TOK_LC;
1933 PN_INIT_LIST(pn4);
1934
1935 if (cx->lint) {
1936 /* must allow fallthru before peeking */
1937 cx->lint->controlCommentsAllowFallthru = JS_TRUE;
1938 cx->lint->controlCommentsHadFallthru = JS_FALSE;
1939 }
1940
1941 while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&
1942 tt != TOK_CASE && tt != TOK_DEFAULT) {
1943 if (tt == TOK_ERROR)
1944 return NULL;
1945
1946 pn5 = Statement(cx, ts, tc);
1947 if (!pn5)
1948 return NULL;
1949
1950 pn4->pn_pos.end = pn5->pn_pos.end;
1951 PN_APPEND(pn4, pn5);
1952 }
1953
1954 if (cx->lint) {
1955 cx->lint->controlCommentsAllowFallthru = JS_FALSE;
1956
1957 if (pn3->pn_type == TOK_DEFAULT && tt != TOK_RC &&
1958 !js_ReportCompileErrorNumber(cx, ts, NULL,
1959 JSREPORT_WARNING | JSREPORT_STRICT,
1960 JSMSG_DEFAULT_NOT_AT_END)) {
1961 return NULL;
1962 }
1963
1964 if (pn4->pn_head) {
1965 if (HasFinalReturn(PN_LAST(pn4), JS_TRUE) == ENDS_IN_OTHER) {
1966 /* must have fallthru or return/break statement on non-empty case */
1967 if (!cx->lint->controlCommentsHadFallthru &&
1968 !js_ReportCompileErrorNumber(cx, ts, NULL,
1969 JSREPORT_WARNING | JSREPORT_STRICT,
1970 (tt == TOK_RC) ? JSMSG_MISSING_BREAK_FOR_LAST_CASE : JSMSG_MISSING_BREAK)) {
1971 return NULL;
1972 }
1973 }
1974 else if (cx->lint->controlCommentsHadFallthru &&
1975 !js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING, JSMSG_INVALID_FALLTHRU)) {
1976 /* fallthru does nothing here */
1977 return NULL;
1978 }
1979 }
1980 else if (tt == TOK_RC && !cx->lint->controlCommentsHadFallthru) {
1981 /* the last statement must have a break */
1982 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
1983 JSREPORT_WARNING | JSREPORT_STRICT,
1984 JSMSG_MISSING_BREAK_FOR_LAST_CASE)) {
1985 return NULL;
1986 }
1987 }
1988 }
1989
1990 /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */
1991 if (pn4->pn_head)
1992 pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin;
1993 pn3->pn_pos.end = pn4->pn_pos.end;
1994 pn3->pn_right = pn4;
1995 }
1996
1997 if (!seenDefault && !js_ReportCompileErrorNumber(cx, ts, NULL,
1998 JSREPORT_WARNING | JSREPORT_STRICT,
1999 JSMSG_MISSING_DEFAULT_CASE)) {
2000 return NULL;
2001 }
2002
2003 js_PopStatement(tc);
2004
2005 pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2006 pn->pn_kid1 = pn1;
2007 pn->pn_kid2 = pn2;
2008 return pn;
2009 }
2010 #endif /* JS_HAS_SWITCH_STATEMENT */
2011
2012 case TOK_WHILE:
2013 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2014 if (!pn)
2015 return NULL;
2016 js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);
2017 pn2 = Condition(cx, ts, tc);
2018 if (!pn2)
2019 return NULL;
2020 pn->pn_left = pn2;
2021 pn2 = Statement(cx, ts, tc);
2022 if (!pn2)
2023 return NULL;
2024 js_PopStatement(tc);
2025 pn->pn_pos.end = pn2->pn_pos.end;
2026 pn->pn_right = pn2;
2027 return pn;
2028
2029 #if JS_HAS_DO_WHILE_LOOP
2030 case TOK_DO:
2031 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2032 if (!pn)
2033 return NULL;
2034 js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);
2035 pn2 = Statement(cx, ts, tc);
2036 if (!pn2)
2037 return NULL;
2038 pn->pn_left = pn2;
2039 MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);
2040 pn2 = Condition(cx, ts, tc);
2041 if (!pn2)
2042 return NULL;
2043 js_PopStatement(tc);
2044 pn->pn_pos.end = pn2->pn_pos.end;
2045 pn->pn_right = pn2;
2046 if (cx->version != JSVERSION_ECMA_3) {
2047 /*
2048 * All legacy and extended versions must do automatic semicolon
2049 * insertion after do-while. See the testcase and discussion in
2050 * http://bugzilla.mozilla.org/show_bug.cgi?id=238945.
2051 */
2052 (void) js_MatchToken(cx, ts, TOK_SEMI);
2053 return pn;
2054 }
2055 break;
2056 #endif /* JS_HAS_DO_WHILE_LOOP */
2057
2058 case TOK_FOR:
2059 /* A FOR node is binary, left is loop control and right is the body. */
2060 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2061 if (!pn)
2062 return NULL;
2063 js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
2064
2065 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
2066 ts->flags |= TSF_OPERAND;
2067 tt = js_PeekToken(cx, ts);
2068 ts->flags &= ~TSF_OPERAND;
2069 if (tt == TOK_SEMI) {
2070 /* No initializer -- set first kid of left sub-node to null. */
2071 pn1 = NULL;
2072 } else {
2073 /* Set pn1 to a var list or an initializing expression. */
2074 #if JS_HAS_IN_OPERATOR
2075 /*
2076 * Set the TCF_IN_FOR_INIT flag during parsing of the first clause
2077 * of the for statement. This flag will be used by the RelExpr
2078 * production; if it is set, then the 'in' keyword will not be
2079 * recognized as an operator, leaving it available to be parsed as
2080 * part of a for/in loop. A side effect of this restriction is
2081 * that (unparenthesized) expressions involving an 'in' operator
2082 * are illegal in the init clause of an ordinary for loop.
2083 */
2084 tc->flags |= TCF_IN_FOR_INIT;
2085 #endif /* JS_HAS_IN_OPERATOR */
2086 if (tt == TOK_VAR) {
2087 (void) js_GetToken(cx, ts);
2088 pn1 = Variables(cx, ts, tc);
2089 } else {
2090 pn1 = Expr(cx, ts, tc);
2091 }
2092 #if JS_HAS_IN_OPERATOR
2093 tc->flags &= ~TCF_IN_FOR_INIT;
2094 #endif /* JS_HAS_IN_OPERATOR */
2095 if (!pn1)
2096 return NULL;
2097 }
2098
2099 /*
2100 * We can be sure that it's a for/in loop if there's still an 'in'
2101 * keyword here, even if JavaScript recognizes 'in' as an operator,
2102 * as we've excluded 'in' from being parsed in RelExpr by setting
2103 * the TCF_IN_FOR_INIT flag in our JSTreeContext.
2104 */
2105 if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
2106 stmtInfo.type = STMT_FOR_IN_LOOP;
2107
2108 /* Check that the left side of the 'in' is valid. */
2109 if ((pn1->pn_type == TOK_VAR)
2110 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)
2111 : (pn1->pn_type != TOK_NAME &&
2112 pn1->pn_type != TOK_DOT &&
2113 pn1->pn_type != TOK_LB)) {
2114 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2115 JSMSG_BAD_FOR_LEFTSIDE);
2116 return NULL;
2117 }
2118
2119 if (pn1->pn_type == TOK_VAR) {
2120 /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */
2121 pn1->pn_extra |= PNX_FORINVAR;
2122
2123 /* Generate a final POP only if the var has an initializer. */
2124 pn2 = pn1->pn_head;
2125 if (pn2->pn_expr)
2126 pn1->pn_extra |= PNX_POPVAR;
2127 } else {
2128 pn2 = pn1;
2129 }
2130
2131 /* Beware 'for (arguments in ...)' with or without a 'var'. */
2132 if (pn2->pn_type == TOK_NAME &&
2133 pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {
2134 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2135 }
2136
2137 /* Parse the object expression as the right operand of 'in'. */
2138 pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
2139 if (!pn2)
2140 return NULL;
2141 pn->pn_left = pn2;
2142 } else {
2143 /* Parse the loop condition or null into pn2. */
2144 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
2145 ts->flags |= TSF_OPERAND;
2146 tt = js_PeekToken(cx, ts);
2147 ts->flags &= ~TSF_OPERAND;
2148 if (tt == TOK_SEMI) {
2149 pn2 = NULL;
2150 } else {
2151 pn2 = Expr(cx, ts, tc);
2152 if (!pn2)
2153 return NULL;
2154 }
2155
2156 /* Parse the update expression or null into pn3. */
2157 MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
2158 ts->flags |= TSF_OPERAND;
2159 tt = js_PeekToken(cx, ts);
2160 ts->flags &= ~TSF_OPERAND;
2161 if (tt == TOK_RP) {
2162 pn3 = NULL;
2163 } else {
2164 tc->flags |= TCF_IN_FOR_POST;
2165 if (cx->lint)
2166 cx->lint->allowIncDec = JS_TRUE;
2167 pn3 = Expr(cx, ts, tc);
2168 if (cx->lint) {
2169 cx->lint->allowIncDec = JS_FALSE;
2170 if (!isIncDec(pn3) && !warnIfHasIncDec(cx, ts, pn3))
2171 return NULL;
2172 }
2173 tc->flags &= ~TCF_IN_FOR_POST;
2174 if (!pn3)
2175 return NULL;
2176 }
2177
2178 /* Build the RESERVED node to use as the left kid of pn. */
2179 pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2180 if (!pn4)
2181 return NULL;
2182 pn4->pn_type = TOK_RESERVED;
2183 pn4->pn_op = JSOP_NOP;
2184 pn4->pn_kid1 = pn1;
2185 pn4->pn_kid2 = pn2;
2186 pn4->pn_kid3 = pn3;
2187 pn->pn_left = pn4;
2188 }
2189
2190 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
2191
2192 /* Parse the loop body into pn->pn_right. */
2193 pn2 = Statement(cx, ts, tc);
2194 if (!pn2)
2195 return NULL;
2196 pn->pn_right = pn2;
2197 js_PopStatement(tc);
2198
2199 /* Record the absolute line number for source note emission. */
2200 pn->pn_pos.end = pn2->pn_pos.end;
2201 return pn;
2202
2203 #if JS_HAS_EXCEPTIONS
2204 case TOK_TRY: {
2205 JSParseNode *catchtail = NULL;
2206 /*
2207 * try nodes are ternary.
2208 * kid1 is the try Statement
2209 * kid2 is the catch node
2210 * kid3 is the finally Statement
2211 *
2212 * catch nodes are ternary.
2213 * kid1 is the discriminant
2214 * kid2 is the next catch node, or NULL
2215 * kid3 is the catch block (on kid3 so that we can always append a
2216 * new catch pn on catchtail->kid2)
2217 *
2218 * catch discriminant nodes are binary
2219 * atom is the receptacle
2220 * expr is the discriminant code
2221 *
2222 * finally nodes are unary (just the finally expression)
2223 */
2224 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2225 pn->pn_op = JSOP_NOP;
2226
2227 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
2228 js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);
2229 pn->pn_kid1 = Statements(cx, ts, tc);
2230 if (!pn->pn_kid1)
2231 return NULL;
2232 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
2233 js_PopStatement(tc);
2234
2235 catchtail = pn;
2236 while (js_PeekToken(cx, ts) == TOK_CATCH) {
2237 /* check for another catch after unconditional catch */
2238 if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {
2239 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2240 JSMSG_CATCH_AFTER_GENERAL);
2241 return NULL;
2242 }
2243
2244 /*
2245 * legal catch forms are:
2246 * catch (v)
2247 * catch (v if <boolean_expression>)
2248 * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)
2249 */
2250 (void) js_GetToken(cx, ts); /* eat `catch' */
2251 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
2252 if (!pn2)
2253 return NULL;
2254
2255 /*
2256 * We use a PN_NAME for the discriminant (catchguard) node
2257 * with the actual discriminant code in the initializer spot
2258 */
2259 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
2260 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);
2261 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2262 if (!pn3)
2263 return NULL;
2264
2265 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
2266 pn3->pn_expr = NULL;
2267 #if JS_HAS_CATCH_GUARD
2268 /*
2269 * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to
2270 * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.
2271 */
2272 if (js_PeekToken(cx, ts) == TOK_IF) {
2273 (void)js_GetToken(cx, ts); /* eat `if' */
2274 pn3->pn_expr = Expr(cx, ts, tc);
2275 if (!pn3->pn_expr)
2276 return NULL;
2277 }
2278 #endif
2279 pn3->pn_attrs |= JSPROP_LINT_DECLARED;
2280 pn2->pn_kid1 = pn3;
2281
2282 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
2283
2284 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
2285 js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);
2286 stmtInfo.label = pn3->pn_atom;
2287 pn2->pn_kid3 = Statements(cx, ts, tc);
2288 if (!pn2->pn_kid3)
2289 return NULL;
2290 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
2291 js_PopStatement(tc);
2292
2293 catchtail = catchtail->pn_kid2 = pn2;
2294 }
2295 catchtail->pn_kid2 = NULL;
2296
2297 if (js_MatchToken(cx, ts, TOK_FINALLY)) {
2298 tc->tryCount++;
2299 MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
2300 js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
2301 pn->pn_kid3 = Statements(cx, ts, tc);
2302 if (!pn->pn_kid3)
2303 return NULL;
2304 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);
2305 js_PopStatement(tc);
2306 } else {
2307 pn->pn_kid3 = NULL;
2308 }
2309 if (!pn->pn_kid2 && !pn->pn_kid3) {
2310 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2311 JSMSG_CATCH_OR_FINALLY);
2312 return NULL;
2313 }
2314 tc->tryCount++;
2315 return pn;
2316 }
2317
2318 case TOK_THROW:
2319 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2320 if (!pn)
2321 return NULL;
2322
2323 /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
2324 ts->flags |= TSF_OPERAND;
2325 tt = js_PeekTokenSameLine(cx, ts);
2326 ts->flags &= ~TSF_OPERAND;
2327 if (tt == TOK_ERROR)
2328 return NULL;
2329 if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) {
2330 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2331 JSMSG_SYNTAX_ERROR);
2332 return NULL;
2333 }
2334
2335 pn2 = Expr(cx, ts, tc);
2336 if (!pn2)
2337 return NULL;
2338 pn->pn_pos.end = pn2->pn_pos.end;
2339 pn->pn_op = JSOP_THROW;
2340 pn->pn_kid = pn2;
2341 break;
2342
2343 /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
2344 case TOK_CATCH:
2345 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2346 JSMSG_CATCH_WITHOUT_TRY);
2347 return NULL;
2348
2349 case TOK_FINALLY:
2350 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2351 JSMSG_FINALLY_WITHOUT_TRY);
2352 return NULL;
2353
2354 #endif /* JS_HAS_EXCEPTIONS */
2355
2356 case TOK_BREAK:
2357 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2358 if (!pn)
2359 return NULL;
2360 if (!MatchLabel(cx, ts, pn))
2361 return NULL;
2362 stmt = tc->topStmt;
2363 label = pn->pn_atom;
2364 if (label) {
2365 for (; ; stmt = stmt->down) {
2366 if (!stmt) {
2367 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2368 JSMSG_LABEL_NOT_FOUND);
2369 return NULL;
2370 }
2371 if (stmt->type == STMT_LABEL && stmt->label == label)
2372 break;
2373 }
2374 } else {
2375 for (; ; stmt = stmt->down) {
2376 if (!stmt) {
2377 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2378 JSMSG_TOUGH_BREAK);
2379 return NULL;
2380 }
2381 if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)
2382 break;
2383 }
2384 }
2385 if (label)
2386 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2387 break;
2388
2389 case TOK_CONTINUE:
2390 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2391 if (!pn)
2392 return NULL;
2393 if (!MatchLabel(cx, ts, pn))
2394 return NULL;
2395 stmt = tc->topStmt;
2396 label = pn->pn_atom;
2397 if (label) {
2398 for (stmt2 = NULL; ; stmt = stmt->down) {
2399 if (!stmt) {
2400 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2401 JSMSG_LABEL_NOT_FOUND);
2402 return NULL;
2403 }
2404 if (stmt->type == STMT_LABEL) {
2405 if (stmt->label == label) {
2406 if (!stmt2 || !STMT_IS_LOOP(stmt2)) {
2407 js_ReportCompileErrorNumber(cx, ts, NULL,
2408 JSREPORT_ERROR,
2409 JSMSG_BAD_CONTINUE);
2410 return NULL;
2411 }
2412 break;
2413 }
2414 } else {
2415 stmt2 = stmt;
2416 }
2417 }
2418 } else {
2419 for (; ; stmt = stmt->down) {
2420 if (!stmt) {
2421 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2422 JSMSG_BAD_CONTINUE);
2423 return NULL;
2424 }
2425 if (STMT_IS_LOOP(stmt))
2426 break;
2427 }
2428 }
2429 if (label)
2430 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
2431 break;
2432
2433 case TOK_WITH:
2434 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2435 JSREPORT_WARNING | JSREPORT_STRICT,
2436 JSMSG_WITH_STATEMENT)) {
2437 return NULL;
2438 }
2439
2440 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
2441 if (!pn)
2442 return NULL;
2443 MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
2444 pn2 = Expr(cx, ts, tc);
2445 if (!pn2)
2446 return NULL;
2447 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);
2448 pn->pn_left = pn2;
2449
2450 js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);
2451 pn2 = Statement(cx, ts, tc);
2452 if (!pn2)
2453 return NULL;
2454 js_PopStatement(tc);
2455
2456 pn->pn_pos.end = pn2->pn_pos.end;
2457 pn->pn_right = pn2;
2458 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2459 return pn;
2460
2461 case TOK_VAR:
2462 pn = Variables(cx, ts, tc);
2463 if (!pn)
2464 return NULL;
2465
2466 /* Tell js_EmitTree to generate a final POP. */
2467 pn->pn_extra |= PNX_POPVAR;
2468 break;
2469
2470 case TOK_RETURN:
2471 if (!(tc->flags & TCF_IN_FUNCTION)) {
2472 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2473 JSMSG_BAD_RETURN);
2474 return NULL;
2475 }
2476 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2477 if (!pn)
2478 return NULL;
2479
2480 /* This is ugly, but we don't want to require a semicolon. */
2481 ts->flags |= TSF_OPERAND;
2482 tt = js_PeekTokenSameLine(cx, ts);
2483 ts->flags &= ~TSF_OPERAND;
2484 if (tt == TOK_ERROR)
2485 return NULL;
2486
2487 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2488 pn2 = Expr(cx, ts, tc);
2489 if (!pn2)
2490 return NULL;
2491 tc->flags |= TCF_RETURN_EXPR;
2492 pn->pn_pos.end = pn2->pn_pos.end;
2493 pn->pn_kid = pn2;
2494 } else {
2495 tc->flags |= TCF_RETURN_VOID;
2496 pn->pn_kid = NULL;
2497 }
2498
2499 if (JS_HAS_STRICT_OPTION(cx) &&
2500 (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
2501 /*
2502 * We must be in a frame with a non-native function, because
2503 * we're compiling one.
2504 */
2505 if (!ReportNoReturnValue(cx, ts))
2506 return NULL;
2507 }
2508 break;
2509
2510 case TOK_LC:
2511 if (cx->lint && (tc->topStmt == NULL || tc->topStmt->type == STMT_BLOCK)) {
2512 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2513 JSREPORT_WARNING |
2514 JSREPORT_STRICT,
2515 JSMSG_MEANINGLESS_BLOCK)) {
2516 return NULL;
2517 }
2518 }
2519 js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
2520 pn = Statements(cx, ts, tc);
2521 if (!pn)
2522 return NULL;
2523
2524 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);
2525 js_PopStatement(tc);
2526 return pn;
2527
2528 case TOK_EOL:
2529 case TOK_SEMI:
2530 if (cx->lint &&
2531 !js_ReportCompileErrorNumber(cx, ts, NULL,
2532 JSREPORT_WARNING |
2533 JSREPORT_STRICT,
2534 tt == TOK_SEMI ?
2535 JSMSG_EMPTY_STATEMENT :
2536 JSMSG_MISSING_SEMICOLON)) {
2537 return NULL;
2538 }
2539
2540 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2541 if (!pn)
2542 return NULL;
2543 pn->pn_type = TOK_SEMI;
2544 pn->pn_kid = NULL;
2545 return pn;
2546
2547 #if JS_HAS_DEBUGGER_KEYWORD
2548 case TOK_DEBUGGER:
2549 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
2550 if (!pn)
2551 return NULL;
2552 pn->pn_type = TOK_DEBUGGER;
2553 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2554 break;
2555 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2556
2557 case TOK_ERROR:
2558 return NULL;
2559
2560 default:
2561 js_UngetToken(ts);
2562
2563 if (cx->lint)
2564 cx->lint->allowIncDec = JS_TRUE;
2565 pn2 = Expr(cx, ts, tc);
2566 if (cx->lint)
2567 cx->lint->allowIncDec = JS_FALSE;
2568
2569 if (!pn2)
2570 return NULL;
2571
2572 if (cx->lint) {
2573 if (!isIncDec(pn2) && !warnIfHasIncDec(cx, ts, pn2))
2574 return NULL;
2575 }
2576
2577 if (js_PeekToken(cx, ts) == TOK_COLON) {
2578 if (pn2->pn_type != TOK_NAME) {
2579 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2580 JSMSG_BAD_LABEL);
2581 return NULL;
2582 }
2583 label = pn2->pn_atom;
2584 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
2585 if (stmt->type == STMT_LABEL && stmt->label == label) {
2586 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2587 JSMSG_DUPLICATE_LABEL);
2588 return NULL;
2589 }
2590 }
2591 (void) js_GetToken(cx, ts);
2592
2593 if (cx->lint &&
2594 !js_ReportCompileErrorNumber(cx, ts, NULL,
2595 JSREPORT_WARNING |
2596 JSREPORT_STRICT,
2597 JSMSG_USE_OF_LABEL)) {
2598 return NULL;
2599 }
2600
2601 /* Push a label struct and parse the statement. */
2602 js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);
2603 stmtInfo.label = label;
2604 pn = Statement(cx, ts, tc);
2605 if (!pn)
2606 return NULL;
2607
2608 /* Pop the label, set pn_expr, and return early. */
2609 js_PopStatement(tc);
2610 pn2->pn_type = TOK_COLON;
2611 pn2->pn_pos.end = pn->pn_pos.end;
2612 pn2->pn_expr = pn;
2613 return pn2;
2614 }
2615
2616 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
2617 if (!pn)
2618 return NULL;
2619 pn->pn_type = TOK_SEMI;
2620 pn->pn_pos = pn2->pn_pos;
2621 pn->pn_kid = pn2;
2622 break;
2623 }
2624
2625 /* Check termination of this primitive statement. */
2626 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
2627 tt = js_PeekTokenSameLine(cx, ts);
2628 if (tt == TOK_ERROR)
2629 return NULL;
2630 if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
2631 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2632 JSMSG_SEMI_BEFORE_STMNT);
2633 return NULL;
2634 }
2635 }
2636
2637 if (!js_MatchToken(cx, ts, TOK_SEMI) && cx->lint) {
2638 if (!cx->lint->lambdaAssignRequiresSemicolon &&
2639 pn &&
2640 pn->pn_type == TOK_SEMI &&
2641 pn->pn_kid &&
2642 pn->pn_kid->pn_type == TOK_ASSIGN &&
2643 pn->pn_kid->pn_right &&
2644 pn->pn_kid->pn_right->pn_type == TOK_FUNCTION &&
2645 pn->pn_kid->pn_right->pn_op == JSOP_ANONFUNOBJ) {
2646 /* disregard missing semicolon */
2647 }
2648 else if (!js_ReportCompileErrorNumber(cx, ts, NULL,
2649 JSREPORT_WARNING |
2650 JSREPORT_STRICT,
2651 JSMSG_MISSING_SEMICOLON)) {
2652 return NULL;
2653 }
2654 }
2655 return pn;
2656 }
2657
2658 static JSParseNode *
Variables(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)2659 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2660 {
2661 JSParseNode *pn, *pn2;
2662 JSObject *obj, *pobj;
2663 JSStackFrame *fp;
2664 JSFunction *fun;
2665 JSClass *clasp;
2666 JSPropertyOp getter, setter, currentGetter, currentSetter;
2667 JSAtom *atom;
2668 JSAtomListElement *ale;
2669 JSOp prevop;
2670 JSProperty *prop;
2671 JSScopeProperty *sprop;
2672 JSBool ok;
2673
2674 /*
2675 * The tricky part of this code is to create special parsenode opcodes for
2676 * getting and setting variables (which will be stored as special slots in
2677 * the frame). The complex special case is an eval() inside a function.
2678 * If the evaluated string references variables in the enclosing function,
2679 * then we need to generate the special variable opcodes. We determine
2680 * this by looking up the variable id in the current variable scope.
2681 */
2682 JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);
2683 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2684 if (!pn)
2685 return NULL;
2686 pn->pn_op = CURRENT_TOKEN(ts).t_op;
2687 pn->pn_extra = 0; /* assume no JSOP_POP needed */
2688 PN_INIT_LIST(pn);
2689
2690 /*
2691 * Skip eval and debugger frames when looking for the function whose code
2692 * is being compiled. If we are called from FunctionBody, TCF_IN_FUNCTION
2693 * will be set in tc->flags, and we can be sure fp->fun is the function to
2694 * use. But if a function calls eval, the string argument is treated as a
2695 * Program (per ECMA), so TCF_IN_FUNCTION won't be set.
2696 *
2697 * What's more, when the following code is reached from eval, cx->fp->fun
2698 * is eval's JSFunction (a native function), so we need to skip its frame.
2699 * We should find the scripted caller's function frame just below it, but
2700 * we code a loop out of paranoia.
2701 */
2702 for (fp = cx->fp; (fp->flags & JSFRAME_SPECIAL) && fp->down; fp = fp->down)
2703 continue;
2704 obj = fp->varobj;
2705 fun = fp->fun;
2706 clasp = OBJ_GET_CLASS(cx, obj);
2707 if (fun && clasp == &js_FunctionClass) {
2708 /* We are compiling code inside a function */
2709 getter = js_GetLocalVariable;
2710 setter = js_SetLocalVariable;
2711 } else if (fun && clasp == &js_CallClass) {
2712 /* We are compiling code from an eval inside a function */
2713 getter = js_GetCallVariable;
2714 setter = js_SetCallVariable;
2715 } else {
2716 getter = clasp->getProperty;
2717 setter = clasp->setProperty;
2718 }
2719
2720 ok = JS_TRUE;
2721 do {
2722 currentGetter = getter;
2723 currentSetter = setter;
2724 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
2725 atom = CURRENT_TOKEN(ts).t_atom;
2726
2727 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
2728 if (ale) {
2729 prevop = ALE_JSOP(ale);
2730 if (JS_HAS_STRICT_OPTION(cx) ||
2731 pn->pn_op == JSOP_DEFCONST ||
2732 prevop == JSOP_DEFCONST) {
2733 const char *name = js_AtomToPrintableString(cx, atom);
2734 if (!name ||
2735 !js_ReportCompileErrorNumber(cx, ts, NULL,
2736 (pn->pn_op != JSOP_DEFCONST &&
2737 prevop != JSOP_DEFCONST)
2738 ? JSREPORT_WARNING |
2739 JSREPORT_STRICT
2740 : JSREPORT_ERROR,
2741 JSMSG_REDECLARED_VAR,
2742 (prevop == JSOP_DEFFUN ||
2743 prevop == JSOP_CLOSURE)
2744 ? js_function_str
2745 : (prevop == JSOP_DEFCONST)
2746 ? js_const_str
2747 : js_var_str,
2748 name)) {
2749 return NULL;
2750 }
2751 }
2752 if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)
2753 tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
2754 } else {
2755 /* save global identifier for lint */
2756 if (cx->lint && cx->lint->scriptIdentifiers && !tc->down) {
2757 jsval val;
2758 val = INT_TO_JSVAL(0);
2759 if (!js_SetProperty(cx, cx->lint->scriptIdentifiers, (jsid)atom, &val))
2760 return NULL;
2761 }
2762 ale = js_IndexAtom(cx, atom, &tc->decls);
2763 if (!ale)
2764 return NULL;
2765 }
2766 ALE_SET_JSOP(ale, pn->pn_op);
2767
2768 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
2769 if (!pn2)
2770 return NULL;
2771 pn2->pn_op = JSOP_NAME;
2772 pn2->pn_atom = atom;
2773 pn2->pn_expr = NULL;
2774 pn2->pn_slot = -1;
2775 pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)
2776 ? JSPROP_ENUMERATE | JSPROP_PERMANENT |
2777 JSPROP_READONLY
2778 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2779 pn2->pn_attrs |= JSPROP_LINT_DECLARED;
2780 PN_APPEND(pn, pn2);
2781
2782 if (!fun) {
2783 prop = NULL; /* don't lookup global variables at compile time */
2784 } else {
2785 if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
2786 return NULL;
2787 }
2788 if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) {
2789 sprop = (JSScopeProperty *)prop;
2790 if (sprop->getter == js_GetArgument) {
2791 const char *name = js_AtomToPrintableString(cx, atom);
2792 if (!name) {
2793 ok = JS_FALSE;
2794 } else if (pn->pn_op == JSOP_DEFCONST) {
2795 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2796 JSMSG_REDECLARED_PARAM,
2797 name);
2798 ok = JS_FALSE;
2799 } else {
2800 currentGetter = js_GetArgument;
2801 currentSetter = js_SetArgument;
2802 ok = js_ReportCompileErrorNumber(cx, ts, NULL,
2803 JSREPORT_WARNING |
2804 JSREPORT_STRICT,
2805 JSMSG_VAR_HIDES_ARG,
2806 name);
2807 }
2808 } else {
2809 if (fun) {
2810 /* Not an argument, must be a redeclared local var. */
2811 if (clasp == &js_FunctionClass) {
2812 JS_ASSERT(sprop->getter == js_GetLocalVariable);
2813 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2814 sprop->shortid < fun->nvars);
2815 } else if (clasp == &js_CallClass) {
2816 if (sprop->getter == js_GetCallVariable) {
2817 /*
2818 * Referencing a variable introduced by a var
2819 * statement in the enclosing function. Check
2820 * that the slot number we have is in range.
2821 */
2822 JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) &&
2823 sprop->shortid < fun->nvars);
2824 } else {
2825 /*
2826 * A variable introduced through another eval:
2827 * don't use the special getters and setters
2828 * since we can't allocate a slot in the frame.
2829 */
2830 currentGetter = sprop->getter;
2831 currentSetter = sprop->setter;
2832 }
2833 }
2834
2835 /* Override the old getter and setter, to handle eval. */
2836 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2837 0, sprop->attrs,
2838 currentGetter,
2839 currentSetter);
2840 if (!sprop)
2841 ok = JS_FALSE;
2842 }
2843 }
2844 } else {
2845 /*
2846 * Property not found in current variable scope: we have not seen
2847 * this variable before. Define a new local variable by adding a
2848 * property to the function's scope, allocating one slot in the
2849 * function's frame. Global variables and any locals declared in
2850 * with statement bodies are handled at runtime, by script prolog
2851 * JSOP_DEFVAR bytecodes generated for slot-less vars.
2852 */
2853 sprop = NULL;
2854 if (prop) {
2855 OBJ_DROP_PROPERTY(cx, pobj, prop);
2856 prop = NULL;
2857 }
2858 if (currentGetter == js_GetCallVariable) {
2859 /* Can't increase fun->nvars in an active frame! */
2860 currentGetter = clasp->getProperty;
2861 currentSetter = clasp->setProperty;
2862 }
2863 if (currentGetter == js_GetLocalVariable &&
2864 atom != cx->runtime->atomState.argumentsAtom &&
2865 fp->scopeChain == obj &&
2866 !js_InWithStatement(tc)) {
2867 if (!js_AddNativeProperty(cx, obj, (jsid)atom,
2868 currentGetter, currentSetter,
2869 SPROP_INVALID_SLOT,
2870 pn2->pn_attrs | JSPROP_SHARED,
2871 SPROP_HAS_SHORTID, fun->nvars)) {
2872 ok = JS_FALSE;
2873 }
2874 fun->nvars++;
2875 }
2876 }
2877
2878 if (js_MatchToken(cx, ts, TOK_ASSIGN)) {
2879 if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
2880 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
2881 JSMSG_BAD_VAR_INIT);
2882 ok = JS_FALSE;
2883 } else {
2884 pn2->pn_expr = AssignExpr(cx, ts, tc);
2885 if (!pn2->pn_expr) {
2886 ok = JS_FALSE;
2887 } else {
2888 pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)
2889 ? JSOP_SETCONST
2890 : JSOP_SETNAME;
2891 if (atom == cx->runtime->atomState.argumentsAtom)
2892 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2893 if (cx->lint && pn2->pn_expr->pn_type == TOK_NAME &&
2894 pn2->pn_expr->pn_atom == atom &&
2895 !js_ReportCompileErrorNumber(cx, ts, NULL,
2896 JSREPORT_WARNING |
2897 JSREPORT_STRICT,
2898 JSMSG_USELESS_ASSIGN)) {
2899 return NULL;
2900 }
2901 }
2902 }
2903 }
2904
2905 if (prop)
2906 OBJ_DROP_PROPERTY(cx, pobj, prop);
2907 if (!ok)
2908 return NULL;
2909 } while (js_MatchToken(cx, ts, TOK_COMMA));
2910
2911 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2912 return pn;
2913 }
2914
2915 static JSParseNode *
Expr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)2916 Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2917 {
2918 JSParseNode *pn, *pn2;
2919
2920 pn = AssignExpr(cx, ts, tc);
2921 if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {
2922 /* allow commas in first and last expressions of for loop */
2923 if (cx->lint && (tc->flags & TCF_IN_FOR_INIT) == 0 && (tc->flags & TCF_IN_FOR_POST) == 0 &&
2924 !js_ReportCompileErrorNumber(cx, ts, NULL,
2925 JSREPORT_WARNING |
2926 JSREPORT_STRICT,
2927 JSMSG_COMMA_SEPARATED_STMTS)) {
2928 return NULL;
2929 }
2930
2931 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
2932 if (!pn2)
2933 return NULL;
2934 pn2->pn_pos.begin = pn->pn_pos.begin;
2935 PN_INIT_LIST_1(pn2, pn);
2936 pn = pn2;
2937 do {
2938 pn2 = AssignExpr(cx, ts, tc);
2939 if (!pn2)
2940 return NULL;
2941 PN_APPEND(pn, pn2);
2942 } while (js_MatchToken(cx, ts, TOK_COMMA));
2943 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
2944 }
2945 return pn;
2946 }
2947
2948 static JSParseNode *
AssignExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)2949 AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
2950 {
2951 JSParseNode *pn, *pn2;
2952 JSTokenType tt;
2953 JSOp op;
2954
2955 CHECK_RECURSION();
2956
2957 pn = CondExpr(cx, ts, tc);
2958 if (!pn)
2959 return NULL;
2960
2961 if (cx->lint && !cx->lint->allowIncDec && !warnIfHasIncDec(cx, ts, pn))
2962 return NULL;
2963
2964 tt = js_GetToken(cx, ts);
2965 #if JS_HAS_GETTER_SETTER
2966 if (tt == TOK_NAME) {
2967 tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);
2968 if (tt == TOK_ERROR)
2969 return NULL;
2970 }
2971 #endif
2972 if (tt != TOK_ASSIGN) {
2973 js_UngetToken(ts);
2974 return pn;
2975 }
2976
2977 op = CURRENT_TOKEN(ts).t_op;
2978 for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)
2979 continue;
2980 switch (pn2->pn_type) {
2981 case TOK_NAME:
2982 pn2->pn_op = JSOP_SETNAME;
2983 if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)
2984 tc->flags |= TCF_FUN_HEAVYWEIGHT;
2985 break;
2986 case TOK_DOT:
2987 pn2->pn_op = JSOP_SETPROP;
2988 break;
2989 case TOK_LB:
2990 pn2->pn_op = JSOP_SETELEM;
2991 break;
2992 #if JS_HAS_LVALUE_RETURN
2993 case TOK_LP:
2994 pn2->pn_op = JSOP_SETCALL;
2995 if (cx->lint &&
2996 !js_ReportCompileErrorNumber(cx, ts, NULL,
2997 JSREPORT_WARNING | JSREPORT_STRICT,
2998 JSMSG_ASSIGN_TO_FUNCTION_CALL)) {
2999 return NULL;
3000 }
3001 break;
3002 #endif
3003 default:
3004 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3005 JSMSG_BAD_LEFTSIDE_OF_ASS);
3006 return NULL;
3007 }
3008 pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);
3009 if (cx->lint && pn &&
3010 pn->pn_left && pn->pn_left->pn_type == TOK_NAME &&
3011 pn->pn_right && pn->pn_right->pn_type == TOK_NAME &&
3012 pn->pn_left->pn_atom == pn->pn_right->pn_atom &&
3013 !js_ReportCompileErrorNumber(cx, ts, NULL,
3014 JSREPORT_WARNING |
3015 JSREPORT_STRICT,
3016 JSMSG_USELESS_ASSIGN)) {
3017 return NULL;
3018 }
3019 return pn;
3020 }
3021
3022 static JSParseNode *
CondExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3023 CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3024 {
3025 JSParseNode *pn, *pn1, *pn2, *pn3;
3026 #if JS_HAS_IN_OPERATOR
3027 uintN oldflags;
3028 #endif /* JS_HAS_IN_OPERATOR */
3029
3030 pn = OrExpr(cx, ts, tc);
3031 if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {
3032 pn1 = pn;
3033 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);
3034 if (!pn)
3035 return NULL;
3036 #if JS_HAS_IN_OPERATOR
3037 /*
3038 * Always accept the 'in' operator in the middle clause of a ternary,
3039 * where it's unambiguous, even if we might be parsing the init of a
3040 * for statement.
3041 */
3042 oldflags = tc->flags;
3043 tc->flags &= ~TCF_IN_FOR_INIT;
3044 #endif /* JS_HAS_IN_OPERATOR */
3045 pn2 = AssignExpr(cx, ts, tc);
3046 #if JS_HAS_IN_OPERATOR
3047 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3048 #endif /* JS_HAS_IN_OPERATOR */
3049
3050 if (!pn2)
3051 return NULL;
3052 MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
3053 pn3 = AssignExpr(cx, ts, tc);
3054 if (!pn3)
3055 return NULL;
3056 pn->pn_pos.begin = pn1->pn_pos.begin;
3057 pn->pn_pos.end = pn3->pn_pos.end;
3058 pn->pn_kid1 = pn1;
3059 pn->pn_kid2 = pn2;
3060 pn->pn_kid3 = pn3;
3061 }
3062 return pn;
3063 }
3064
3065 static JSParseNode *
OrExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3066 OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3067 {
3068 JSParseNode *pn;
3069
3070 pn = AndExpr(cx, ts, tc);
3071 if (pn && js_MatchToken(cx, ts, TOK_OR))
3072 pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);
3073 return pn;
3074 }
3075
3076 static JSParseNode *
AndExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3077 AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3078 {
3079 JSParseNode *pn;
3080
3081 pn = BitOrExpr(cx, ts, tc);
3082 if (pn && js_MatchToken(cx, ts, TOK_AND))
3083 pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);
3084 return pn;
3085 }
3086
3087 static JSParseNode *
BitOrExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3088 BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3089 {
3090 JSParseNode *pn;
3091
3092 pn = BitXorExpr(cx, ts, tc);
3093 while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {
3094 pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),
3095 tc);
3096 }
3097 return pn;
3098 }
3099
3100 static JSParseNode *
BitXorExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3101 BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3102 {
3103 JSParseNode *pn;
3104
3105 pn = BitAndExpr(cx, ts, tc);
3106 while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {
3107 pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),
3108 tc);
3109 }
3110 return pn;
3111 }
3112
3113 static JSParseNode *
BitAndExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3114 BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3115 {
3116 JSParseNode *pn;
3117
3118 pn = EqExpr(cx, ts, tc);
3119 while (pn && js_MatchToken(cx, ts, TOK_BITAND))
3120 pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);
3121 return pn;
3122 }
3123
3124 static JSBool
AllowImplicitConversionOnCompare(JSContext * cx,JSParseNode * pn)3125 AllowImplicitConversionOnCompare(JSContext *cx, JSParseNode *pn)
3126 {
3127 if (pn->pn_type == TOK_PRIMARY &&
3128 (pn->pn_op == JSOP_NULL || pn->pn_op == JSOP_TRUE || pn->pn_op == JSOP_FALSE)) {
3129 return JS_FALSE;
3130 }
3131
3132 if (pn->pn_type == TOK_STRING && pn->pn_op == JSOP_STRING &&
3133 JSSTRING_LENGTH(ATOM_TO_STRING(pn->pn_atom)) == 0) {
3134 return JS_FALSE;
3135 }
3136
3137 if (pn->pn_type == TOK_NUMBER && pn->pn_dval == 0) {
3138 return JS_FALSE;
3139 }
3140
3141 return JS_TRUE;
3142 }
3143
3144 static JSParseNode *
EqExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3145 EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3146 {
3147 JSParseNode *pn;
3148 JSOp op;
3149
3150 pn = RelExpr(cx, ts, tc);
3151 while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {
3152 op = CURRENT_TOKEN(ts).t_op;
3153 pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);
3154
3155 if (cx->lint && pn && pn->pn_left && pn->pn_right) {
3156 if (pn->pn_op != JSOP_NEW_EQ && pn->pn_op != JSOP_NEW_NE &&
3157 (!AllowImplicitConversionOnCompare(cx, pn->pn_left) ||
3158 !AllowImplicitConversionOnCompare(cx, pn->pn_right))) {
3159
3160 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
3161 JSREPORT_WARNING |
3162 JSREPORT_STRICT,
3163 JSMSG_COMPARISON_TYPE_CONV)) {
3164 return NULL;
3165 }
3166 }
3167 if (AreExpressionsIdentical(cx, pn->pn_left, pn->pn_right, JS_FALSE) &&
3168 !js_ReportCompileErrorNumber(cx, ts, NULL,
3169 JSREPORT_WARNING |
3170 JSREPORT_STRICT,
3171 JSMSG_USELESS_COMPARISON)) {
3172 return NULL;
3173 }
3174 }
3175 }
3176 return pn;
3177 }
3178
3179 static JSParseNode *
RelExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3180 RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3181 {
3182 JSParseNode *pn;
3183 JSTokenType tt;
3184 JSOp op;
3185 #if JS_HAS_IN_OPERATOR
3186 uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT;
3187
3188 /*
3189 * Uses of the in operator in ShiftExprs are always unambiguous,
3190 * so unset the flag that prohibits recognizing it.
3191 */
3192 tc->flags &= ~TCF_IN_FOR_INIT;
3193 #endif /* JS_HAS_IN_OPERATOR */
3194
3195 pn = ShiftExpr(cx, ts, tc);
3196 while (pn &&
3197 (js_MatchToken(cx, ts, TOK_RELOP)
3198 #if JS_HAS_IN_OPERATOR
3199 /*
3200 * Recognize the 'in' token as an operator only if we're not
3201 * currently in the init expr of a for loop.
3202 */
3203 || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))
3204 #endif /* JS_HAS_IN_OPERATOR */
3205 #if JS_HAS_INSTANCEOF
3206 || js_MatchToken(cx, ts, TOK_INSTANCEOF)
3207 #endif /* JS_HAS_INSTANCEOF */
3208 )) {
3209 tt = CURRENT_TOKEN(ts).type;
3210 op = CURRENT_TOKEN(ts).t_op;
3211 pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);
3212
3213 if (cx->lint && pn && pn->pn_left && pn->pn_right &&
3214 AreExpressionsIdentical(cx, pn->pn_left, pn->pn_right, JS_FALSE) &&
3215 !js_ReportCompileErrorNumber(cx, ts, NULL,
3216 JSREPORT_WARNING |
3217 JSREPORT_STRICT,
3218 JSMSG_USELESS_COMPARISON)) {
3219 return NULL;
3220 }
3221 }
3222 #if JS_HAS_IN_OPERATOR
3223 /* Restore previous state of inForInit flag. */
3224 tc->flags |= inForInitFlag;
3225 #endif /* JS_HAS_IN_OPERATOR */
3226
3227 return pn;
3228 }
3229
3230 static JSParseNode *
ShiftExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3231 ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3232 {
3233 JSParseNode *pn;
3234 JSOp op;
3235
3236 pn = AddExpr(cx, ts, tc);
3237 while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {
3238 op = CURRENT_TOKEN(ts).t_op;
3239 pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);
3240 }
3241 return pn;
3242 }
3243
3244 static JSParseNode *
AddExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3245 AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3246 {
3247 JSParseNode *pn;
3248 JSTokenType tt;
3249 JSOp op;
3250
3251 pn = MulExpr(cx, ts, tc);
3252 while (pn &&
3253 (js_MatchToken(cx, ts, TOK_PLUS) ||
3254 js_MatchToken(cx, ts, TOK_MINUS))) {
3255 tt = CURRENT_TOKEN(ts).type;
3256 op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;
3257 pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);
3258 }
3259 return pn;
3260 }
3261
3262 static JSParseNode *
MulExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3263 MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3264 {
3265 JSParseNode *pn;
3266 JSTokenType tt;
3267 JSOp op;
3268
3269 pn = UnaryExpr(cx, ts, tc);
3270 while (pn &&
3271 (js_MatchToken(cx, ts, TOK_STAR) ||
3272 js_MatchToken(cx, ts, TOK_DIVOP))) {
3273 tt = CURRENT_TOKEN(ts).type;
3274 op = CURRENT_TOKEN(ts).t_op;
3275 pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);
3276 }
3277 return pn;
3278 }
3279
3280 static JSParseNode *
SetLvalKid(JSContext * cx,JSTokenStream * ts,JSParseNode * pn,JSParseNode * kid,const char * name)3281 SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
3282 const char *name)
3283 {
3284 while (kid->pn_type == TOK_RP)
3285 kid = kid->pn_kid;
3286 if (kid->pn_type != TOK_NAME &&
3287 kid->pn_type != TOK_DOT &&
3288 #if JS_HAS_LVALUE_RETURN
3289 (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) &&
3290 #endif
3291 kid->pn_type != TOK_LB) {
3292 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3293 JSMSG_BAD_OPERAND, name);
3294 return NULL;
3295 }
3296 pn->pn_kid = kid;
3297 return kid;
3298 }
3299
3300 static const char *incop_name_str[] = {"increment", "decrement"};
3301
3302 static JSBool
SetIncOpKid(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * pn,JSParseNode * kid,JSTokenType tt,JSBool preorder)3303 SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3304 JSParseNode *pn, JSParseNode *kid,
3305 JSTokenType tt, JSBool preorder)
3306 {
3307 JSOp op;
3308
3309 kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);
3310 if (!kid)
3311 return JS_FALSE;
3312 switch (kid->pn_type) {
3313 case TOK_NAME:
3314 op = (tt == TOK_INC)
3315 ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)
3316 : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);
3317 if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)
3318 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3319 break;
3320
3321 case TOK_DOT:
3322 op = (tt == TOK_INC)
3323 ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)
3324 : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);
3325 break;
3326
3327 #if JS_HAS_LVALUE_RETURN
3328 case TOK_LP:
3329 kid->pn_op = JSOP_SETCALL;
3330 #endif
3331 case TOK_LB:
3332 op = (tt == TOK_INC)
3333 ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)
3334 : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);
3335 break;
3336
3337 default:
3338 JS_ASSERT(0);
3339 op = JSOP_NOP;
3340 }
3341 pn->pn_op = op;
3342 return JS_TRUE;
3343 }
3344
3345 static JSParseNode *
UnaryExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3346 UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3347 {
3348 JSTokenType tt;
3349 JSParseNode *pn, *pn2;
3350
3351 ts->flags |= TSF_OPERAND;
3352 tt = js_GetToken(cx, ts);
3353 ts->flags &= ~TSF_OPERAND;
3354
3355 switch (tt) {
3356 case TOK_UNARYOP:
3357 case TOK_PLUS:
3358 case TOK_MINUS:
3359 if (cx->lint && tt == TOK_UNARYOP && CURRENT_TOKEN(ts).t_op == JSOP_VOID &&
3360 !js_ReportCompileErrorNumber(cx, ts, NULL,
3361 JSREPORT_WARNING |
3362 JSREPORT_STRICT,
3363 JSMSG_USELESS_VOID)) {
3364 return NULL;
3365 }
3366
3367 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3368 if (!pn)
3369 return NULL;
3370 pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */
3371 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3372 pn2 = UnaryExpr(cx, ts, tc);
3373 if (!pn2)
3374 return NULL;
3375 pn->pn_pos.end = pn2->pn_pos.end;
3376 pn->pn_kid = pn2;
3377 break;
3378
3379 case TOK_INC:
3380 case TOK_DEC:
3381 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3382 if (!pn)
3383 return NULL;
3384 pn2 = MemberExpr(cx, ts, tc, JS_TRUE);
3385 if (!pn2)
3386 return NULL;
3387 if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))
3388 return NULL;
3389 pn->pn_pos.end = pn2->pn_pos.end;
3390 break;
3391
3392 case TOK_DELETE:
3393 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3394 if (!pn)
3395 return NULL;
3396 pn2 = UnaryExpr(cx, ts, tc);
3397 if (!pn2)
3398 return NULL;
3399 pn->pn_pos.end = pn2->pn_pos.end;
3400
3401 /*
3402 * Under ECMA3, deleting any unary expression is valid -- it simply
3403 * returns true. Here we strip off any parentheses.
3404 */
3405 while (pn2->pn_type == TOK_RP)
3406 pn2 = pn2->pn_kid;
3407 pn->pn_kid = pn2;
3408 break;
3409
3410 case TOK_ERROR:
3411 return NULL;
3412
3413 default:
3414 js_UngetToken(ts);
3415 pn = MemberExpr(cx, ts, tc, JS_TRUE);
3416 if (!pn)
3417 return NULL;
3418
3419 /* Don't look across a newline boundary for a postfix incop. */
3420 if (ON_CURRENT_LINE(ts, pn->pn_pos)) {
3421 tt = js_PeekTokenSameLine(cx, ts);
3422 if (tt == TOK_INC || tt == TOK_DEC) {
3423 (void) js_GetToken(cx, ts);
3424 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3425 if (!pn2)
3426 return NULL;
3427 if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))
3428 return NULL;
3429 pn2->pn_pos.begin = pn->pn_pos.begin;
3430 pn = pn2;
3431
3432 if (cx->lint && ((tt == TOK_INC && js_PeekToken(cx, ts) == TOK_PLUS) ||
3433 (tt == TOK_DEC && js_PeekToken(cx, ts) == TOK_MINUS)) &&
3434 !js_ReportCompileErrorNumber(cx, ts, NULL,
3435 JSREPORT_WARNING |
3436 JSREPORT_STRICT,
3437 JSMSG_MULTIPLE_PLUS_MINUS))
3438 {
3439 return NULL;
3440 }
3441 }
3442 }
3443 break;
3444 }
3445 return pn;
3446 }
3447
3448 static JSBool
ArgumentList(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSParseNode * listNode)3449 ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3450 JSParseNode *listNode)
3451 {
3452 JSBool matched;
3453
3454 ts->flags |= TSF_OPERAND;
3455 matched = js_MatchToken(cx, ts, TOK_RP);
3456 ts->flags &= ~TSF_OPERAND;
3457 if (!matched) {
3458 do {
3459 JSParseNode *argNode = AssignExpr(cx, ts, tc);
3460 if (!argNode)
3461 return JS_FALSE;
3462 PN_APPEND(listNode, argNode);
3463 } while (js_MatchToken(cx, ts, TOK_COMMA));
3464
3465 if (js_GetToken(cx, ts) != TOK_RP) {
3466 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3467 JSMSG_PAREN_AFTER_ARGS);
3468 return JS_FALSE;
3469 }
3470 }
3471 return JS_TRUE;
3472 }
3473
3474 static JSParseNode *
MemberExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc,JSBool allowCallSyntax)3475 MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
3476 JSBool allowCallSyntax)
3477 {
3478 JSParseNode *pn, *pn2, *pn3;
3479 JSTokenType tt;
3480
3481 CHECK_RECURSION();
3482
3483 /* Check for new expression first. */
3484 ts->flags |= TSF_OPERAND;
3485 tt = js_PeekToken(cx, ts);
3486 ts->flags &= ~TSF_OPERAND;
3487 if (tt == TOK_NEW) {
3488 (void) js_GetToken(cx, ts);
3489
3490 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
3491 if (!pn)
3492 return NULL;
3493 pn2 = MemberExpr(cx, ts, tc, JS_FALSE);
3494 if (!pn2)
3495 return NULL;
3496 pn->pn_op = JSOP_NEW;
3497 PN_INIT_LIST_1(pn, pn2);
3498 pn->pn_pos.begin = pn2->pn_pos.begin;
3499
3500 if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn))
3501 return NULL;
3502 if (pn->pn_count > ARGC_LIMIT) {
3503 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3504 JSMSG_TOO_MANY_CON_ARGS);
3505 return NULL;
3506 }
3507 pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
3508 } else {
3509 pn = PrimaryExpr(cx, ts, tc);
3510 if (!pn)
3511 return NULL;
3512 }
3513
3514 while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {
3515 if (tt == TOK_DOT) {
3516 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
3517 if (!pn2)
3518 return NULL;
3519 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
3520 pn2->pn_pos.begin = pn->pn_pos.begin;
3521 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3522 pn2->pn_op = JSOP_GETPROP;
3523 pn2->pn_expr = pn;
3524 pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
3525 } else if (tt == TOK_LB) {
3526 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
3527 if (!pn2)
3528 return NULL;
3529 pn3 = Expr(cx, ts, tc);
3530 if (!pn3)
3531 return NULL;
3532
3533 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
3534 pn2->pn_pos.begin = pn->pn_pos.begin;
3535 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3536
3537 /* Optimize o['p'] to o.p by rewriting pn2. */
3538 if (pn3->pn_type == TOK_STRING) {
3539 pn2->pn_type = TOK_DOT;
3540 pn2->pn_op = JSOP_GETPROP;
3541 pn2->pn_arity = PN_NAME;
3542 pn2->pn_expr = pn;
3543 pn2->pn_atom = pn3->pn_atom;
3544 } else {
3545 pn2->pn_op = JSOP_GETELEM;
3546 pn2->pn_left = pn;
3547 pn2->pn_right = pn3;
3548 }
3549 } else if (allowCallSyntax && tt == TOK_LP) {
3550 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
3551 if (!pn2)
3552 return NULL;
3553
3554 /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */
3555 pn2->pn_op = JSOP_CALL;
3556 if (pn->pn_op == JSOP_NAME &&
3557 pn->pn_atom == cx->runtime->atomState.evalAtom) {
3558 pn2->pn_op = JSOP_EVAL;
3559 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3560 }
3561
3562 PN_INIT_LIST_1(pn2, pn);
3563 pn2->pn_pos.begin = pn->pn_pos.begin;
3564
3565 if (!ArgumentList(cx, ts, tc, pn2))
3566 return NULL;
3567 if (pn2->pn_count > ARGC_LIMIT) {
3568 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3569 JSMSG_TOO_MANY_FUN_ARGS);
3570 return NULL;
3571 }
3572 if (pn->pn_op == JSOP_NAME &&
3573 pn->pn_atom == cx->runtime->atomState.lazy.parseIntAtom && pn2->pn_count <= 2 &&
3574 !js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT,
3575 JSMSG_PARSEINT_MISSING_RADIX,
3576 js_AtomToPrintableString(cx, pn->pn_atom))) {
3577 return NULL;
3578 }
3579 pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3580 } else {
3581 js_UngetToken(ts);
3582 return pn;
3583 }
3584
3585 pn = pn2;
3586 }
3587 if (tt == TOK_ERROR)
3588 return NULL;
3589 return pn;
3590 }
3591
3592 static JSParseNode *
PrimaryExpr(JSContext * cx,JSTokenStream * ts,JSTreeContext * tc)3593 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
3594 {
3595 JSTokenType tt;
3596 JSParseNode *pn, *pn2, *pn3;
3597 char *badWord;
3598 #if JS_HAS_GETTER_SETTER
3599 JSAtom *atom;
3600 JSRuntime *rt;
3601 #endif
3602
3603 #if JS_HAS_SHARP_VARS
3604 JSParseNode *defsharp;
3605 JSBool notsharp;
3606
3607 defsharp = NULL;
3608 notsharp = JS_FALSE;
3609 again:
3610 /*
3611 * Control flows here after #n= is scanned. If the following primary is
3612 * not valid after such a "sharp variable" definition, the tt switch case
3613 * should set notsharp.
3614 */
3615 #endif
3616
3617 CHECK_RECURSION();
3618
3619 ts->flags |= TSF_OPERAND;
3620 tt = js_GetToken(cx, ts);
3621 ts->flags &= ~TSF_OPERAND;
3622
3623 #if JS_HAS_GETTER_SETTER
3624 if (tt == TOK_NAME) {
3625 tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
3626 if (tt == TOK_ERROR)
3627 return NULL;
3628 }
3629 #endif
3630
3631 switch (tt) {
3632 #if JS_HAS_LEXICAL_CLOSURE
3633 case TOK_FUNCTION:
3634 pn = FunctionExpr(cx, ts, tc);
3635 if (!pn)
3636 return NULL;
3637 break;
3638 #endif
3639
3640 #if JS_HAS_INITIALIZERS
3641 case TOK_LB:
3642 {
3643 JSBool matched;
3644 jsuint atomIndex;
3645
3646 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
3647 if (!pn)
3648 return NULL;
3649 pn->pn_type = TOK_RB;
3650 pn->pn_extra = 0;
3651
3652 #if JS_HAS_SHARP_VARS
3653 if (defsharp) {
3654 PN_INIT_LIST_1(pn, defsharp);
3655 defsharp = NULL;
3656 } else
3657 #endif
3658 PN_INIT_LIST(pn);
3659
3660 ts->flags |= TSF_OPERAND;
3661 matched = js_MatchToken(cx, ts, TOK_RB);
3662 ts->flags &= ~TSF_OPERAND;
3663 if (!matched) {
3664 for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {
3665 ts->flags |= TSF_OPERAND;
3666 tt = js_PeekToken(cx, ts);
3667 ts->flags &= ~TSF_OPERAND;
3668 if (tt == TOK_RB) {
3669 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
3670 JSREPORT_WARNING |
3671 JSREPORT_STRICT,
3672 JSMSG_TRAILING_COMMA_IN_ARRAY)) {
3673 return NULL;
3674 }
3675 pn->pn_extra |= PNX_ENDCOMMA;
3676 break;
3677 }
3678
3679 if (tt == TOK_COMMA) {
3680 /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */
3681 js_MatchToken(cx, ts, TOK_COMMA);
3682 pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3683 } else {
3684 pn2 = AssignExpr(cx, ts, tc);
3685 }
3686 if (!pn2)
3687 return NULL;
3688 PN_APPEND(pn, pn2);
3689
3690 if (tt != TOK_COMMA) {
3691 /* If we didn't already match TOK_COMMA in above case. */
3692 if (!js_MatchToken(cx, ts, TOK_COMMA))
3693 break;
3694 }
3695 }
3696
3697 MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
3698 }
3699 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3700 return pn;
3701 }
3702
3703 case TOK_LC:
3704 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
3705 if (!pn)
3706 return NULL;
3707 pn->pn_type = TOK_RC;
3708
3709 #if JS_HAS_SHARP_VARS
3710 if (defsharp) {
3711 PN_INIT_LIST_1(pn, defsharp);
3712 defsharp = NULL;
3713 } else
3714 #endif
3715 PN_INIT_LIST(pn);
3716
3717 if (!js_MatchToken(cx, ts, TOK_RC)) {
3718 do {
3719 JSOp op;
3720
3721 tt = js_GetToken(cx, ts);
3722 switch (tt) {
3723 case TOK_NUMBER:
3724 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3725 if (pn3)
3726 pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
3727 break;
3728 case TOK_NAME:
3729 #if JS_HAS_GETTER_SETTER
3730 atom = CURRENT_TOKEN(ts).t_atom;
3731 rt = cx->runtime;
3732 if (atom == rt->atomState.getAtom ||
3733 atom == rt->atomState.setAtom) {
3734 op = (atom == rt->atomState.getAtom)
3735 ? JSOP_GETTER
3736 : JSOP_SETTER;
3737 if (js_MatchToken(cx, ts, TOK_NAME)) {
3738 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME,
3739 tc);
3740 if (!pn3)
3741 return NULL;
3742 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3743 pn3->pn_expr = NULL;
3744
3745 /* We have to fake a 'function' token here. */
3746 CURRENT_TOKEN(ts).t_op = JSOP_NOP;
3747 CURRENT_TOKEN(ts).type = TOK_FUNCTION;
3748 pn2 = FunctionExpr(cx, ts, tc);
3749 pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);
3750 goto skip;
3751 }
3752 }
3753 /* else fall thru ... */
3754 #endif
3755 case TOK_STRING:
3756 pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3757 if (pn3)
3758 pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
3759 break;
3760 case TOK_RC:
3761 if (!js_ReportCompileErrorNumber(cx, ts, NULL,
3762 JSREPORT_WARNING |
3763 JSREPORT_STRICT,
3764 JSMSG_TRAILING_COMMA)) {
3765 return NULL;
3766 }
3767 goto end_obj_init;
3768 default:
3769 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3770 JSMSG_BAD_PROP_ID);
3771 return NULL;
3772 }
3773
3774 tt = js_GetToken(cx, ts);
3775 #if JS_HAS_GETTER_SETTER
3776 if (tt == TOK_NAME) {
3777 tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
3778 if (tt == TOK_ERROR)
3779 return NULL;
3780 }
3781 #endif
3782 if (tt != TOK_COLON) {
3783 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3784 JSMSG_COLON_AFTER_ID);
3785 return NULL;
3786 }
3787 op = CURRENT_TOKEN(ts).t_op;
3788 pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
3789 tc);
3790 pn3->pn_attrs |= JSPROP_LINT_DECLARED;
3791 #if JS_HAS_GETTER_SETTER
3792 skip:
3793 #endif
3794 if (!pn2)
3795 return NULL;
3796 PN_APPEND(pn, pn2);
3797 } while (js_MatchToken(cx, ts, TOK_COMMA));
3798
3799 MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
3800 }
3801 end_obj_init:
3802 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3803 return pn;
3804
3805 #if JS_HAS_SHARP_VARS
3806 case TOK_DEFSHARP:
3807 if (defsharp)
3808 goto badsharp;
3809 defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3810 if (!defsharp)
3811 return NULL;
3812 defsharp->pn_kid = NULL;
3813 defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
3814 goto again;
3815
3816 case TOK_USESHARP:
3817 /* Check for forward/dangling references at runtime, to allow eval. */
3818 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3819 if (!pn)
3820 return NULL;
3821 pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
3822 notsharp = JS_TRUE;
3823 break;
3824 #endif /* JS_HAS_SHARP_VARS */
3825 #endif /* JS_HAS_INITIALIZERS */
3826
3827 case TOK_LP:
3828 {
3829 #if JS_HAS_IN_OPERATOR
3830 uintN oldflags;
3831 #endif
3832 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);
3833 if (!pn)
3834 return NULL;
3835 #if JS_HAS_IN_OPERATOR
3836 /*
3837 * Always accept the 'in' operator in a parenthesized expression,
3838 * where it's unambiguous, even if we might be parsing the init of a
3839 * for statement.
3840 */
3841 oldflags = tc->flags;
3842 tc->flags &= ~TCF_IN_FOR_INIT;
3843 #endif
3844 pn2 = Expr(cx, ts, tc);
3845 #if JS_HAS_IN_OPERATOR
3846 tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
3847 #endif
3848 if (!pn2)
3849 return NULL;
3850
3851 MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
3852 pn->pn_type = TOK_RP;
3853 pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
3854 pn->pn_kid = pn2;
3855 break;
3856 }
3857
3858 case TOK_STRING:
3859 #if JS_HAS_SHARP_VARS
3860 notsharp = JS_TRUE;
3861 #endif
3862 /* FALL THROUGH */
3863 case TOK_NAME:
3864 case TOK_OBJECT:
3865 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3866 if (!pn)
3867 return NULL;
3868 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3869 pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
3870 if (tt == TOK_NAME) {
3871 pn->pn_arity = PN_NAME;
3872 pn->pn_expr = NULL;
3873 pn->pn_slot = -1;
3874 pn->pn_attrs = 0;
3875
3876 /* Unqualified __parent__ and __proto__ uses require activations. */
3877 if (pn->pn_atom == cx->runtime->atomState.parentAtom ||
3878 pn->pn_atom == cx->runtime->atomState.protoAtom) {
3879 tc->flags |= TCF_FUN_HEAVYWEIGHT;
3880 } else {
3881 JSAtomListElement *ale;
3882 JSStackFrame *fp;
3883 JSStmtInfo *stmt;
3884
3885 /* Measure optimizable global variable uses. */
3886 ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom);
3887 if (ale &&
3888 !(fp = cx->fp)->fun &&
3889 fp->scopeChain == fp->varobj &&
3890 !js_InWithStatement(tc) &&
3891 !js_InCatchBlock(tc, pn->pn_atom)) {
3892 tc->globalUses++;
3893 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
3894 if (STMT_IS_LOOP(stmt)) {
3895 tc->loopyGlobalUses++;
3896 break;
3897 }
3898 }
3899 }
3900 }
3901
3902 if (cx->lint && !MarkIfDeclared(cx, ts, tc, pn))
3903 return NULL;
3904 }
3905 break;
3906
3907 case TOK_NUMBER:
3908 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3909 if (!pn)
3910 return NULL;
3911 pn->pn_dval = CURRENT_TOKEN(ts).t_dval;
3912 #if JS_HAS_SHARP_VARS
3913 notsharp = JS_TRUE;
3914 #endif
3915 break;
3916
3917 case TOK_PRIMARY:
3918 pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);
3919 if (!pn)
3920 return NULL;
3921 pn->pn_op = CURRENT_TOKEN(ts).t_op;
3922 #if JS_HAS_SHARP_VARS
3923 notsharp = JS_TRUE;
3924 #endif
3925 break;
3926
3927 #if !JS_HAS_EXPORT_IMPORT
3928 case TOK_EXPORT:
3929 case TOK_IMPORT:
3930 #endif
3931 case TOK_RESERVED:
3932 badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr,
3933 (size_t) CURRENT_TOKEN(ts).pos.end.index
3934 - CURRENT_TOKEN(ts).pos.begin.index);
3935 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3936 JSMSG_RESERVED_ID, badWord);
3937 JS_free(cx, badWord);
3938 return NULL;
3939
3940 case TOK_ERROR:
3941 /* The scanner or one of its subroutines reported the error. */
3942 return NULL;
3943
3944 default:
3945 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3946 JSMSG_SYNTAX_ERROR);
3947 return NULL;
3948 }
3949
3950 #if JS_HAS_SHARP_VARS
3951 if (defsharp) {
3952 if (notsharp) {
3953 badsharp:
3954 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
3955 JSMSG_BAD_SHARP_VAR_DEF);
3956 return NULL;
3957 }
3958 defsharp->pn_kid = pn;
3959 return defsharp;
3960 }
3961 #endif
3962 return pn;
3963 }
3964
3965 static JSBool
ContainsVarStmt(JSParseNode * pn)3966 ContainsVarStmt(JSParseNode *pn)
3967 {
3968 JSParseNode *pn2;
3969
3970 if (!pn)
3971 return JS_FALSE;
3972 switch (pn->pn_arity) {
3973 case PN_LIST:
3974 if (pn->pn_type == TOK_VAR)
3975 return JS_TRUE;
3976 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3977 if (ContainsVarStmt(pn2))
3978 return JS_TRUE;
3979 }
3980 break;
3981 case PN_TERNARY:
3982 return ContainsVarStmt(pn->pn_kid1) ||
3983 ContainsVarStmt(pn->pn_kid2) ||
3984 ContainsVarStmt(pn->pn_kid3);
3985 case PN_BINARY:
3986 /*
3987 * Limit recursion if pn is a binary expression, which can't contain a
3988 * var statement.
3989 */
3990 if (pn->pn_op != JSOP_NOP)
3991 return JS_FALSE;
3992 return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
3993 case PN_UNARY:
3994 if (pn->pn_op != JSOP_NOP)
3995 return JS_FALSE;
3996 return ContainsVarStmt(pn->pn_kid);
3997 default:;
3998 }
3999 return JS_FALSE;
4000 }
4001
4002 /*
4003 * Fold from one constant type to another.
4004 * XXX handles only strings and numbers for now
4005 */
4006 static JSBool
FoldType(JSContext * cx,JSParseNode * pn,JSTokenType type)4007 FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type)
4008 {
4009 if (pn->pn_type != type) {
4010 switch (type) {
4011 case TOK_NUMBER:
4012 if (pn->pn_type == TOK_STRING) {
4013 jsdouble d;
4014 if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d))
4015 return JS_FALSE;
4016 pn->pn_dval = d;
4017 pn->pn_type = TOK_NUMBER;
4018 pn->pn_op = JSOP_NUMBER;
4019 }
4020 break;
4021
4022 case TOK_STRING:
4023 if (pn->pn_type == TOK_NUMBER) {
4024 JSString *str = js_NumberToString(cx, pn->pn_dval);
4025 if (!str)
4026 return JS_FALSE;
4027 pn->pn_atom = js_AtomizeString(cx, str, 0);
4028 if (!pn->pn_atom)
4029 return JS_FALSE;
4030 pn->pn_type = TOK_STRING;
4031 pn->pn_op = JSOP_STRING;
4032 }
4033 break;
4034
4035 default:;
4036 }
4037 }
4038 return JS_TRUE;
4039 }
4040
4041 /*
4042 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
4043 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
4044 * a successful call to this function.
4045 */
4046 static JSBool
FoldBinaryNumeric(JSContext * cx,JSOp op,JSParseNode * pn1,JSParseNode * pn2,JSParseNode * pn,JSTreeContext * tc)4047 FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2,
4048 JSParseNode *pn, JSTreeContext *tc)
4049 {
4050 jsdouble d, d2;
4051 int32 i, j;
4052 uint32 u;
4053
4054 JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER);
4055 d = pn1->pn_dval;
4056 d2 = pn2->pn_dval;
4057 switch (op) {
4058 case JSOP_LSH:
4059 case JSOP_RSH:
4060 if (!js_DoubleToECMAInt32(cx, d, &i))
4061 return JS_FALSE;
4062 if (!js_DoubleToECMAInt32(cx, d2, &j))
4063 return JS_FALSE;
4064 j &= 31;
4065 d = (op == JSOP_LSH) ? i << j : i >> j;
4066 break;
4067
4068 case JSOP_URSH:
4069 if (!js_DoubleToECMAUint32(cx, d, &u))
4070 return JS_FALSE;
4071 if (!js_DoubleToECMAInt32(cx, d2, &j))
4072 return JS_FALSE;
4073 j &= 31;
4074 d = u >> j;
4075 break;
4076
4077 case JSOP_ADD:
4078 d += d2;
4079 break;
4080
4081 case JSOP_SUB:
4082 d -= d2;
4083 break;
4084
4085 case JSOP_MUL:
4086 d *= d2;
4087 break;
4088
4089 case JSOP_DIV:
4090 if (d2 == 0) {
4091 #if defined(XP_WIN)
4092 /* XXX MSVC miscompiles such that (NaN == 0) */
4093 if (JSDOUBLE_IS_NaN(d2))
4094 d = *cx->runtime->jsNaN;
4095 else
4096 #endif
4097 if (d == 0 || JSDOUBLE_IS_NaN(d))
4098 d = *cx->runtime->jsNaN;
4099 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
4100 d = *cx->runtime->jsNegativeInfinity;
4101 else
4102 d = *cx->runtime->jsPositiveInfinity;
4103 } else {
4104 d /= d2;
4105 }
4106 break;
4107
4108 case JSOP_MOD:
4109 if (d2 == 0) {
4110 d = *cx->runtime->jsNaN;
4111 } else {
4112 #if defined(XP_WIN)
4113 /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
4114 if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
4115 #endif
4116 d = fmod(d, d2);
4117 }
4118 break;
4119
4120 default:;
4121 }
4122
4123 /* Take care to allow pn1 or pn2 to alias pn. */
4124 if (pn1 != pn)
4125 RecycleTree(pn1, tc);
4126 if (pn2 != pn)
4127 RecycleTree(pn2, tc);
4128 pn->pn_type = TOK_NUMBER;
4129 pn->pn_op = JSOP_NUMBER;
4130 pn->pn_arity = PN_NULLARY;
4131 pn->pn_dval = d;
4132 return JS_TRUE;
4133 }
4134
4135 JSBool
js_FoldConstants(JSContext * cx,JSParseNode * pn,JSTreeContext * tc)4136 js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
4137 {
4138 JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
4139 int stackDummy;
4140
4141 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
4142 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
4143 return JS_FALSE;
4144 }
4145
4146 switch (pn->pn_arity) {
4147 case PN_FUNC:
4148 if (!js_FoldConstants(cx, pn->pn_body, tc))
4149 return JS_FALSE;
4150 break;
4151
4152 case PN_LIST:
4153 /* Save the list head in pn1 for later use. */
4154 for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4155 if (!js_FoldConstants(cx, pn2, tc))
4156 return JS_FALSE;
4157 }
4158 break;
4159
4160 case PN_TERNARY:
4161 /* Any kid may be null (e.g. for (;;)). */
4162 pn1 = pn->pn_kid1;
4163 pn2 = pn->pn_kid2;
4164 pn3 = pn->pn_kid3;
4165 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4166 return JS_FALSE;
4167 if (pn2 && !js_FoldConstants(cx, pn2, tc))
4168 return JS_FALSE;
4169 if (pn3 && !js_FoldConstants(cx, pn3, tc))
4170 return JS_FALSE;
4171 break;
4172
4173 case PN_BINARY:
4174 /* First kid may be null (for default case in switch). */
4175 pn1 = pn->pn_left;
4176 pn2 = pn->pn_right;
4177 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4178 return JS_FALSE;
4179 if (!js_FoldConstants(cx, pn2, tc))
4180 return JS_FALSE;
4181 break;
4182
4183 case PN_UNARY:
4184 /* Our kid may be null (e.g. return; vs. return e;). */
4185 pn1 = pn->pn_kid;
4186 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4187 return JS_FALSE;
4188 break;
4189
4190 case PN_NAME:
4191 /*
4192 * Skip pn1 down along a chain of dotted member expressions to avoid
4193 * excessive recursion. Our only goal here is to fold constants (if
4194 * any) in the primary expression operand to the left of the first
4195 * dot in the chain.
4196 */
4197 pn1 = pn->pn_expr;
4198 while (pn1 && pn1->pn_arity == PN_NAME)
4199 pn1 = pn1->pn_expr;
4200 if (pn1 && !js_FoldConstants(cx, pn1, tc))
4201 return JS_FALSE;
4202 break;
4203
4204 case PN_NULLARY:
4205 break;
4206 }
4207
4208 switch (pn->pn_type) {
4209 case TOK_IF:
4210 if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
4211 break;
4212 /* FALL THROUGH */
4213
4214 case TOK_HOOK:
4215 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
4216 switch (pn1->pn_type) {
4217 case TOK_NUMBER:
4218 if (pn1->pn_dval == 0)
4219 pn2 = pn3;
4220 break;
4221 case TOK_STRING:
4222 if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0)
4223 pn2 = pn3;
4224 break;
4225 case TOK_PRIMARY:
4226 if (pn1->pn_op == JSOP_TRUE)
4227 break;
4228 if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
4229 pn2 = pn3;
4230 break;
4231 }
4232 /* FALL THROUGH */
4233 default:
4234 /* Early return to dodge common code that copies pn2 to pn. */
4235 return JS_TRUE;
4236 }
4237
4238 if (pn2) {
4239 /* pn2 is the then- or else-statement subtree to compile. */
4240 PN_MOVE_NODE(pn, pn2);
4241 } else {
4242 /* False condition and no else: make pn an empty statement. */
4243 pn->pn_type = TOK_SEMI;
4244 pn->pn_arity = PN_UNARY;
4245 pn->pn_kid = NULL;
4246 }
4247 RecycleTree(pn2, tc);
4248 if (pn3 && pn3 != pn2)
4249 RecycleTree(pn3, tc);
4250 break;
4251
4252 case TOK_PLUS:
4253 if (pn->pn_arity == PN_LIST) {
4254 size_t length, length2;
4255 jschar *chars;
4256 JSString *str, *str2;
4257
4258 /*
4259 * Any string literal term with all others number or string means
4260 * this is a concatenation. If any term is not a string or number
4261 * literal, we can't fold.
4262 */
4263 JS_ASSERT(pn->pn_count > 2);
4264 if (pn->pn_extra & PNX_CANTFOLD)
4265 return JS_TRUE;
4266 if (pn->pn_extra != PNX_STRCAT)
4267 goto do_binary_op;
4268
4269 /* Ok, we're concatenating: convert non-string constant operands. */
4270 length = 0;
4271 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4272 if (!FoldType(cx, pn2, TOK_STRING))
4273 return JS_FALSE;
4274 /* XXX fold only if all operands convert to string */
4275 if (pn2->pn_type != TOK_STRING)
4276 return JS_TRUE;
4277 length += ATOM_TO_STRING(pn2->pn_atom)->length;
4278 }
4279
4280 /* Allocate a new buffer and string descriptor for the result. */
4281 chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
4282 if (!chars)
4283 return JS_FALSE;
4284 str = js_NewString(cx, chars, length, 0);
4285 if (!str) {
4286 JS_free(cx, chars);
4287 return JS_FALSE;
4288 }
4289
4290 /* Fill the buffer, advancing chars and recycling kids as we go. */
4291 for (pn2 = pn1; pn2; pn2 = pn3) {
4292 str2 = ATOM_TO_STRING(pn2->pn_atom);
4293 length2 = str2->length;
4294 js_strncpy(chars, str2->chars, length2);
4295 chars += length2;
4296 pn3 = pn2->pn_next;
4297 RecycleTree(pn2, tc);
4298 }
4299 *chars = 0;
4300
4301 /* Atomize the result string and mutate pn to refer to it. */
4302 pn->pn_atom = js_AtomizeString(cx, str, 0);
4303 if (!pn->pn_atom)
4304 return JS_FALSE;
4305 pn->pn_type = TOK_STRING;
4306 pn->pn_op = JSOP_STRING;
4307 pn->pn_arity = PN_NULLARY;
4308 break;
4309 }
4310
4311 /* Handle a binary string concatenation. */
4312 JS_ASSERT(pn->pn_arity == PN_BINARY);
4313 if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) {
4314 JSString *left, *right, *str;
4315
4316 if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2,
4317 TOK_STRING)) {
4318 return JS_FALSE;
4319 }
4320 if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING)
4321 return JS_TRUE;
4322 left = ATOM_TO_STRING(pn1->pn_atom);
4323 right = ATOM_TO_STRING(pn2->pn_atom);
4324 str = js_ConcatStrings(cx, left, right);
4325 if (!str)
4326 return JS_FALSE;
4327 pn->pn_atom = js_AtomizeString(cx, str, 0);
4328 if (!pn->pn_atom)
4329 return JS_FALSE;
4330 pn->pn_type = TOK_STRING;
4331 pn->pn_op = JSOP_STRING;
4332 pn->pn_arity = PN_NULLARY;
4333 RecycleTree(pn1, tc);
4334 RecycleTree(pn2, tc);
4335 break;
4336 }
4337
4338 /* Can't concatenate string literals, let's try numbers. */
4339 goto do_binary_op;
4340
4341 case TOK_STAR:
4342 /* The * in 'import *;' parses as a nullary star node. */
4343 if (pn->pn_arity == PN_NULLARY)
4344 break;
4345 /* FALL THROUGH */
4346
4347 case TOK_SHOP:
4348 case TOK_MINUS:
4349 case TOK_DIVOP:
4350 do_binary_op:
4351 if (pn->pn_arity == PN_LIST) {
4352 JS_ASSERT(pn->pn_count > 2);
4353 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
4354 if (!FoldType(cx, pn2, TOK_NUMBER))
4355 return JS_FALSE;
4356 /* XXX fold only if all operands convert to number */
4357 if (pn2->pn_type != TOK_NUMBER)
4358 break;
4359 }
4360 if (!pn2) {
4361 JSOp op = pn->pn_op;
4362
4363 pn2 = pn1->pn_next;
4364 pn3 = pn2->pn_next;
4365 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
4366 return JS_FALSE;
4367 while ((pn2 = pn3) != NULL) {
4368 pn3 = pn2->pn_next;
4369 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
4370 return JS_FALSE;
4371 }
4372 }
4373 } else {
4374 JS_ASSERT(pn->pn_arity == PN_BINARY);
4375 if (!FoldType(cx, pn1, TOK_NUMBER) ||
4376 !FoldType(cx, pn2, TOK_NUMBER)) {
4377 return JS_FALSE;
4378 }
4379 if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {
4380 if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc))
4381 return JS_FALSE;
4382 }
4383 }
4384 break;
4385
4386 case TOK_UNARYOP:
4387 if (pn1->pn_type == TOK_NUMBER) {
4388 jsdouble d;
4389 int32 i;
4390
4391 /* Operate on one numeric constant. */
4392 d = pn1->pn_dval;
4393 switch (pn->pn_op) {
4394 case JSOP_BITNOT:
4395 if (!js_DoubleToECMAInt32(cx, d, &i))
4396 return JS_FALSE;
4397 d = ~i;
4398 break;
4399
4400 case JSOP_NEG:
4401 #ifdef HPUX
4402 /*
4403 * Negation of a zero doesn't produce a negative
4404 * zero on HPUX. Perform the operation by bit
4405 * twiddling.
4406 */
4407 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
4408 #else
4409 d = -d;
4410 #endif
4411 break;
4412
4413 case JSOP_POS:
4414 break;
4415
4416 case JSOP_NOT:
4417 pn->pn_type = TOK_PRIMARY;
4418 pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;
4419 pn->pn_arity = PN_NULLARY;
4420 /* FALL THROUGH */
4421
4422 default:
4423 /* Return early to dodge the common TOK_NUMBER code. */
4424 return JS_TRUE;
4425 }
4426 pn->pn_type = TOK_NUMBER;
4427 pn->pn_op = JSOP_NUMBER;
4428 pn->pn_arity = PN_NULLARY;
4429 pn->pn_dval = d;
4430 RecycleTree(pn1, tc);
4431 }
4432 break;
4433
4434 default:;
4435 }
4436
4437 return JS_TRUE;
4438 }
4439