~vaifrax/inkscape/bugfix170049

« back to all changes in this revision

Viewing changes to src/dom/js/jsparse.c

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

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