~ubuntu-branches/ubuntu/jaunty/couchdb/jaunty

« back to all changes in this revision

Viewing changes to src/js/jsemit.c

  • Committer: Bazaar Package Importer
  • Author(s): Noah Slater
  • Date: 2008-05-24 16:30:21 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080524163021-bpkh6s1090i37xy1
Tags: 0.7.3~svn650270-2
* Added release partitioning to database and log directories.
* Corrected postrm maintainer script to not remove logs.

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
 
 * vim: set ts=8 sw=4 et tw=78:
3
 
 *
4
 
 * ***** BEGIN LICENSE BLOCK *****
5
 
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6
 
 *
7
 
 * The contents of this file are subject to the Mozilla Public License Version
8
 
 * 1.1 (the "License"); you may not use this file except in compliance with
9
 
 * the License. You may obtain a copy of the License at
10
 
 * http://www.mozilla.org/MPL/
11
 
 *
12
 
 * Software distributed under the License is distributed on an "AS IS" basis,
13
 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14
 
 * for the specific language governing rights and limitations under the
15
 
 * License.
16
 
 *
17
 
 * The Original Code is Mozilla Communicator client code, released
18
 
 * March 31, 1998.
19
 
 *
20
 
 * The Initial Developer of the Original Code is
21
 
 * Netscape Communications Corporation.
22
 
 * Portions created by the Initial Developer are Copyright (C) 1998
23
 
 * the Initial Developer. All Rights Reserved.
24
 
 *
25
 
 * Contributor(s):
26
 
 *
27
 
 * Alternatively, the contents of this file may be used under the terms of
28
 
 * either of the GNU General Public License Version 2 or later (the "GPL"),
29
 
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30
 
 * in which case the provisions of the GPL or the LGPL are applicable instead
31
 
 * of those above. If you wish to allow use of your version of this file only
32
 
 * under the terms of either the GPL or the LGPL, and not to allow others to
33
 
 * use your version of this file under the terms of the MPL, indicate your
34
 
 * decision by deleting the provisions above and replace them with the notice
35
 
 * and other provisions required by the GPL or the LGPL. If you do not delete
36
 
 * the provisions above, a recipient may use your version of this file under
37
 
 * the terms of any one of the MPL, the GPL or the LGPL.
38
 
 *
39
 
 * ***** END LICENSE BLOCK ***** */
40
 
 
41
 
/*
42
 
 * JS bytecode generation.
43
 
 */
44
 
#include "jsstddef.h"
45
 
#ifdef HAVE_MEMORY_H
46
 
#include <memory.h>
47
 
#endif
48
 
#include <string.h>
49
 
#include "jstypes.h"
50
 
#include "jsarena.h" /* Added by JSIFY */
51
 
#include "jsutil.h" /* Added by JSIFY */
52
 
#include "jsbit.h"
53
 
#include "jsprf.h"
54
 
#include "jsapi.h"
55
 
#include "jsatom.h"
56
 
#include "jscntxt.h"
57
 
#include "jsconfig.h"
58
 
#include "jsemit.h"
59
 
#include "jsfun.h"
60
 
#include "jsnum.h"
61
 
#include "jsopcode.h"
62
 
#include "jsparse.h"
63
 
#include "jsregexp.h"
64
 
#include "jsscan.h"
65
 
#include "jsscope.h"
66
 
#include "jsscript.h"
67
 
 
68
 
/* Allocation chunk counts, must be powers of two in general. */
69
 
#define BYTECODE_CHUNK  256     /* code allocation increment */
70
 
#define SRCNOTE_CHUNK   64      /* initial srcnote allocation increment */
71
 
#define TRYNOTE_CHUNK   64      /* trynote allocation increment */
72
 
 
73
 
/* Macros to compute byte sizes from typed element counts. */
74
 
#define BYTECODE_SIZE(n)        ((n) * sizeof(jsbytecode))
75
 
#define SRCNOTE_SIZE(n)         ((n) * sizeof(jssrcnote))
76
 
#define TRYNOTE_SIZE(n)         ((n) * sizeof(JSTryNote))
77
 
 
78
 
JS_FRIEND_API(JSBool)
79
 
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
80
 
                     JSArenaPool *codePool, JSArenaPool *notePool,
81
 
                     const char *filename, uintN lineno,
82
 
                     JSPrincipals *principals)
83
 
{
84
 
    memset(cg, 0, sizeof *cg);
85
 
    TREE_CONTEXT_INIT(&cg->treeContext);
86
 
    cg->treeContext.flags |= TCF_COMPILING;
87
 
    cg->codePool = codePool;
88
 
    cg->notePool = notePool;
89
 
    cg->codeMark = JS_ARENA_MARK(codePool);
90
 
    cg->noteMark = JS_ARENA_MARK(notePool);
91
 
    cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
92
 
    cg->current = &cg->main;
93
 
    cg->filename = filename;
94
 
    cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno;
95
 
    cg->principals = principals;
96
 
    ATOM_LIST_INIT(&cg->atomList);
97
 
    cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1;
98
 
    ATOM_LIST_INIT(&cg->constList);
99
 
    return JS_TRUE;
100
 
}
101
 
 
102
 
JS_FRIEND_API(void)
103
 
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
104
 
{
105
 
    TREE_CONTEXT_FINISH(&cg->treeContext);
106
 
    JS_ARENA_RELEASE(cg->codePool, cg->codeMark);
107
 
    JS_ARENA_RELEASE(cg->notePool, cg->noteMark);
108
 
    JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);
109
 
}
110
 
 
111
 
static ptrdiff_t
112
 
EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)
113
 
{
114
 
    jsbytecode *base, *limit, *next;
115
 
    ptrdiff_t offset, length;
116
 
    size_t incr, size;
117
 
 
118
 
    base = CG_BASE(cg);
119
 
    next = CG_NEXT(cg);
120
 
    limit = CG_LIMIT(cg);
121
 
    offset = PTRDIFF(next, base, jsbytecode);
122
 
    if (next + delta > limit) {
123
 
        length = offset + delta;
124
 
        length = (length <= BYTECODE_CHUNK)
125
 
                 ? BYTECODE_CHUNK
126
 
                 : JS_BIT(JS_CeilingLog2(length));
127
 
        incr = BYTECODE_SIZE(length);
128
 
        if (!base) {
129
 
            JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
130
 
        } else {
131
 
            size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
132
 
            incr -= size;
133
 
            JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
134
 
        }
135
 
        if (!base) {
136
 
            JS_ReportOutOfMemory(cx);
137
 
            return -1;
138
 
        }
139
 
        CG_BASE(cg) = base;
140
 
        CG_LIMIT(cg) = base + length;
141
 
        CG_NEXT(cg) = base + offset;
142
 
    }
143
 
    return offset;
144
 
}
145
 
 
146
 
static void
147
 
UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
148
 
{
149
 
    jsbytecode *pc;
150
 
    const JSCodeSpec *cs;
151
 
    intN nuses;
152
 
 
153
 
    pc = CG_CODE(cg, target);
154
 
    cs = &js_CodeSpec[pc[0]];
155
 
    nuses = cs->nuses;
156
 
    if (nuses < 0)
157
 
        nuses = 2 + GET_ARGC(pc);       /* stack: fun, this, [argc arguments] */
158
 
    cg->stackDepth -= nuses;
159
 
    JS_ASSERT(cg->stackDepth >= 0);
160
 
    if (cg->stackDepth < 0) {
161
 
        char numBuf[12];
162
 
        JS_snprintf(numBuf, sizeof numBuf, "%d", target);
163
 
        JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING,
164
 
                                     js_GetErrorMessage, NULL,
165
 
                                     JSMSG_STACK_UNDERFLOW,
166
 
                                     cg->filename ? cg->filename : "stdin",
167
 
                                     numBuf);
168
 
    }
169
 
    cg->stackDepth += cs->ndefs;
170
 
    if ((uintN)cg->stackDepth > cg->maxStackDepth)
171
 
        cg->maxStackDepth = cg->stackDepth;
172
 
}
173
 
 
174
 
ptrdiff_t
175
 
js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
176
 
{
177
 
    ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
178
 
 
179
 
    if (offset >= 0) {
180
 
        *CG_NEXT(cg)++ = (jsbytecode)op;
181
 
        UpdateDepth(cx, cg, offset);
182
 
    }
183
 
    return offset;
184
 
}
185
 
 
186
 
ptrdiff_t
187
 
js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)
188
 
{
189
 
    ptrdiff_t offset = EmitCheck(cx, cg, op, 2);
190
 
 
191
 
    if (offset >= 0) {
192
 
        jsbytecode *next = CG_NEXT(cg);
193
 
        next[0] = (jsbytecode)op;
194
 
        next[1] = op1;
195
 
        CG_NEXT(cg) = next + 2;
196
 
        UpdateDepth(cx, cg, offset);
197
 
    }
198
 
    return offset;
199
 
}
200
 
 
201
 
ptrdiff_t
202
 
js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,
203
 
         jsbytecode op2)
204
 
{
205
 
    ptrdiff_t offset = EmitCheck(cx, cg, op, 3);
206
 
 
207
 
    if (offset >= 0) {
208
 
        jsbytecode *next = CG_NEXT(cg);
209
 
        next[0] = (jsbytecode)op;
210
 
        next[1] = op1;
211
 
        next[2] = op2;
212
 
        CG_NEXT(cg) = next + 3;
213
 
        UpdateDepth(cx, cg, offset);
214
 
    }
215
 
    return offset;
216
 
}
217
 
 
218
 
ptrdiff_t
219
 
js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
220
 
{
221
 
    ptrdiff_t length = 1 + (ptrdiff_t)extra;
222
 
    ptrdiff_t offset = EmitCheck(cx, cg, op, length);
223
 
 
224
 
    if (offset >= 0) {
225
 
        jsbytecode *next = CG_NEXT(cg);
226
 
        *next = (jsbytecode)op;
227
 
        memset(next + 1, 0, BYTECODE_SIZE(extra));
228
 
        CG_NEXT(cg) = next + length;
229
 
        UpdateDepth(cx, cg, offset);
230
 
    }
231
 
    return offset;
232
 
}
233
 
 
234
 
/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
235
 
const char js_with_statement_str[] = "with statement";
236
 
const char js_finally_block_str[]  = "finally block";
237
 
const char js_script_str[]         = "script";
238
 
 
239
 
static const char *statementName[] = {
240
 
    "label statement",       /* LABEL */
241
 
    "if statement",          /* IF */
242
 
    "else statement",        /* ELSE */
243
 
    "switch statement",      /* SWITCH */
244
 
    "block",                 /* BLOCK */
245
 
    js_with_statement_str,   /* WITH */
246
 
    "catch block",           /* CATCH */
247
 
    "try block",             /* TRY */
248
 
    js_finally_block_str,    /* FINALLY */
249
 
    js_finally_block_str,    /* SUBROUTINE */
250
 
    "do loop",               /* DO_LOOP */
251
 
    "for loop",              /* FOR_LOOP */
252
 
    "for/in loop",           /* FOR_IN_LOOP */
253
 
    "while loop",            /* WHILE_LOOP */
254
 
};
255
 
 
256
 
static const char *
257
 
StatementName(JSCodeGenerator *cg)
258
 
{
259
 
    if (!cg->treeContext.topStmt)
260
 
        return js_script_str;
261
 
    return statementName[cg->treeContext.topStmt->type];
262
 
}
263
 
 
264
 
static void
265
 
ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)
266
 
{
267
 
    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
268
 
                         StatementName(cg));
269
 
}
270
 
 
271
 
/**
272
 
  Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP)
273
 
  and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided
274
 
  into unconditional (gotos and gosubs), and conditional jumps or branches
275
 
  (which pop a value, test it, and jump depending on its value).  Most jumps
276
 
  have just one immediate operand, a signed offset from the jump opcode's pc
277
 
  to the target bytecode.  The lookup and table switch opcodes may contain
278
 
  many jump offsets.
279
 
 
280
 
  Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was
281
 
  fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is
282
 
  suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for
283
 
  the extended form of the JSOP_OR branch opcode).  The unextended or short
284
 
  formats have 16-bit signed immediate offset operands, the extended or long
285
 
  formats have 32-bit signed immediates.  The span-dependency problem consists
286
 
  of selecting as few long instructions as possible, or about as few -- since
287
 
  jumps can span other jumps, extending one jump may cause another to need to
288
 
  be extended.
289
 
 
290
 
  Most JS scripts are short, so need no extended jumps.  We optimize for this
291
 
  case by generating short jumps until we know a long jump is needed.  After
292
 
  that point, we keep generating short jumps, but each jump's 16-bit immediate
293
 
  offset operand is actually an unsigned index into cg->spanDeps, an array of
294
 
  JSSpanDep structs.  Each struct tells the top offset in the script of the
295
 
  opcode, the "before" offset of the jump (which will be the same as top for
296
 
  simplex jumps, but which will index further into the bytecode array for a
297
 
  non-initial jump offset in a lookup or table switch), the after "offset"
298
 
  adjusted during span-dependent instruction selection (initially the same
299
 
  value as the "before" offset), and the jump target (more below).
300
 
 
301
 
  Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must
302
 
  ensure that all bytecode generated so far can be inspected to discover where
303
 
  the jump offset immediate operands lie within CG_CODE(cg).  But the bonus is
304
 
  that we generate span-dependency records sorted by their offsets, so we can
305
 
  binary-search when trying to find a JSSpanDep for a given bytecode offset,
306
 
  or the nearest JSSpanDep at or above a given pc.
307
 
 
308
 
  To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows
309
 
  65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand.  This
310
 
  tells us that we need to binary-search for the cg->spanDeps entry by the
311
 
  jump opcode's bytecode offset (sd->before).
312
 
 
313
 
  Jump targets need to be maintained in a data structure that lets us look
314
 
  up an already-known target by its address (jumps may have a common target),
315
 
  and that also lets us update the addresses (script-relative, a.k.a. absolute
316
 
  offsets) of targets that come after a jump target (for when a jump below
317
 
  that target needs to be extended).  We use an AVL tree, implemented using
318
 
  recursion, but with some tricky optimizations to its height-balancing code
319
 
  (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html).
320
 
 
321
 
  A final wrinkle: backpatch chains are linked by jump-to-jump offsets with
322
 
  positive sign, even though they link "backward" (i.e., toward lower bytecode
323
 
  address).  We don't want to waste space and search time in the AVL tree for
324
 
  such temporary backpatch deltas, so we use a single-bit wildcard scheme to
325
 
  tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas
326
 
  in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known
327
 
  target, or is still awaiting backpatching.
328
 
 
329
 
  Note that backpatch chains would present a problem for BuildSpanDepTable,
330
 
  which inspects bytecode to build cg->spanDeps on demand, when the first
331
 
  short jump offset overflows.  To solve this temporary problem, we emit a
332
 
  proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose
333
 
  nuses/ndefs counts help keep the stack balanced, but whose opcode format
334
 
  distinguishes its backpatch delta immediate operand from a normal jump
335
 
  offset.
336
 
 */
337
 
static int
338
 
BalanceJumpTargets(JSJumpTarget **jtp)
339
 
{
340
 
    JSJumpTarget *jt, *jt2, *root;
341
 
    int dir, otherDir, heightChanged;
342
 
    JSBool doubleRotate;
343
 
 
344
 
    jt = *jtp;
345
 
    JS_ASSERT(jt->balance != 0);
346
 
 
347
 
    if (jt->balance < -1) {
348
 
        dir = JT_RIGHT;
349
 
        doubleRotate = (jt->kids[JT_LEFT]->balance > 0);
350
 
    } else if (jt->balance > 1) {
351
 
        dir = JT_LEFT;
352
 
        doubleRotate = (jt->kids[JT_RIGHT]->balance < 0);
353
 
    } else {
354
 
        return 0;
355
 
    }
356
 
 
357
 
    otherDir = JT_OTHER_DIR(dir);
358
 
    if (doubleRotate) {
359
 
        jt2 = jt->kids[otherDir];
360
 
        *jtp = root = jt2->kids[dir];
361
 
 
362
 
        jt->kids[otherDir] = root->kids[dir];
363
 
        root->kids[dir] = jt;
364
 
 
365
 
        jt2->kids[dir] = root->kids[otherDir];
366
 
        root->kids[otherDir] = jt2;
367
 
 
368
 
        heightChanged = 1;
369
 
        root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0);
370
 
        root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0);
371
 
        root->balance = 0;
372
 
    } else {
373
 
        *jtp = root = jt->kids[otherDir];
374
 
        jt->kids[otherDir] = root->kids[dir];
375
 
        root->kids[dir] = jt;
376
 
 
377
 
        heightChanged = (root->balance != 0);
378
 
        jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance);
379
 
    }
380
 
 
381
 
    return heightChanged;
382
 
}
383
 
 
384
 
typedef struct AddJumpTargetArgs {
385
 
    JSContext           *cx;
386
 
    JSCodeGenerator     *cg;
387
 
    ptrdiff_t           offset;
388
 
    JSJumpTarget        *node;
389
 
} AddJumpTargetArgs;
390
 
 
391
 
static int
392
 
AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp)
393
 
{
394
 
    JSJumpTarget *jt;
395
 
    int balanceDelta;
396
 
 
397
 
    jt = *jtp;
398
 
    if (!jt) {
399
 
        JSCodeGenerator *cg = args->cg;
400
 
 
401
 
        jt = cg->jtFreeList;
402
 
        if (jt) {
403
 
            cg->jtFreeList = jt->kids[JT_LEFT];
404
 
        } else {
405
 
            JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool,
406
 
                                   sizeof *jt);
407
 
            if (!jt) {
408
 
                JS_ReportOutOfMemory(args->cx);
409
 
                return 0;
410
 
            }
411
 
        }
412
 
        jt->offset = args->offset;
413
 
        jt->balance = 0;
414
 
        jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL;
415
 
        cg->numJumpTargets++;
416
 
        args->node = jt;
417
 
        *jtp = jt;
418
 
        return 1;
419
 
    }
420
 
 
421
 
    if (jt->offset == args->offset) {
422
 
        args->node = jt;
423
 
        return 0;
424
 
    }
425
 
 
426
 
    if (args->offset < jt->offset)
427
 
        balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]);
428
 
    else
429
 
        balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]);
430
 
    if (!args->node)
431
 
        return 0;
432
 
 
433
 
    jt->balance += balanceDelta;
434
 
    return (balanceDelta && jt->balance)
435
 
           ? 1 - BalanceJumpTargets(jtp)
436
 
           : 0;
437
 
}
438
 
 
439
 
#ifdef DEBUG_brendan
440
 
static int AVLCheck(JSJumpTarget *jt)
441
 
{
442
 
    int lh, rh;
443
 
 
444
 
    if (!jt) return 0;
445
 
    JS_ASSERT(-1 <= jt->balance && jt->balance <= 1);
446
 
    lh = AVLCheck(jt->kids[JT_LEFT]);
447
 
    rh = AVLCheck(jt->kids[JT_RIGHT]);
448
 
    JS_ASSERT(jt->balance == rh - lh);
449
 
    return 1 + JS_MAX(lh, rh);
450
 
}
451
 
#endif
452
 
 
453
 
static JSBool
454
 
SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd,
455
 
                 ptrdiff_t off)
456
 
{
457
 
    AddJumpTargetArgs args;
458
 
 
459
 
    if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) {
460
 
        ReportStatementTooLarge(cx, cg);
461
 
        return JS_FALSE;
462
 
    }
463
 
 
464
 
    args.cx = cx;
465
 
    args.cg = cg;
466
 
    args.offset = sd->top + off;
467
 
    args.node = NULL;
468
 
    AddJumpTarget(&args, &cg->jumpTargets);
469
 
    if (!args.node)
470
 
        return JS_FALSE;
471
 
 
472
 
#ifdef DEBUG_brendan
473
 
    AVLCheck(cg->jumpTargets);
474
 
#endif
475
 
 
476
 
    SD_SET_TARGET(sd, args.node);
477
 
    return JS_TRUE;
478
 
}
479
 
 
480
 
#define SPANDEPS_MIN            256
481
 
#define SPANDEPS_SIZE(n)        ((n) * sizeof(JSSpanDep))
482
 
#define SPANDEPS_SIZE_MIN       SPANDEPS_SIZE(SPANDEPS_MIN)
483
 
 
484
 
static JSBool
485
 
AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2,
486
 
           ptrdiff_t off)
487
 
{
488
 
    uintN index;
489
 
    JSSpanDep *sdbase, *sd;
490
 
    size_t size;
491
 
 
492
 
    index = cg->numSpanDeps;
493
 
    if (index + 1 == 0) {
494
 
        ReportStatementTooLarge(cx, cg);
495
 
        return JS_FALSE;
496
 
    }
497
 
 
498
 
    if ((index & (index - 1)) == 0 &&
499
 
        (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) {
500
 
        if (!sdbase) {
501
 
            size = SPANDEPS_SIZE_MIN;
502
 
            JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size);
503
 
        } else {
504
 
            size = SPANDEPS_SIZE(index);
505
 
            JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size);
506
 
        }
507
 
        if (!sdbase)
508
 
            return JS_FALSE;
509
 
        cg->spanDeps = sdbase;
510
 
    }
511
 
 
512
 
    cg->numSpanDeps = index + 1;
513
 
    sd = cg->spanDeps + index;
514
 
    sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode);
515
 
    sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode);
516
 
 
517
 
    if (js_CodeSpec[*pc].format & JOF_BACKPATCH) {
518
 
        /* Jump offset will be backpatched if off is a non-zero "bpdelta". */
519
 
        if (off != 0) {
520
 
            JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN);
521
 
            if (off > BPDELTA_MAX) {
522
 
                ReportStatementTooLarge(cx, cg);
523
 
                return JS_FALSE;
524
 
            }
525
 
        }
526
 
        SD_SET_BPDELTA(sd, off);
527
 
    } else if (off == 0) {
528
 
        /* Jump offset will be patched directly, without backpatch chaining. */
529
 
        SD_SET_TARGET(sd, NULL);
530
 
    } else {
531
 
        /* The jump offset in off is non-zero, therefore it's already known. */
532
 
        if (!SetSpanDepTarget(cx, cg, sd, off))
533
 
            return JS_FALSE;
534
 
    }
535
 
 
536
 
    if (index > SPANDEP_INDEX_MAX)
537
 
        index = SPANDEP_INDEX_HUGE;
538
 
    SET_SPANDEP_INDEX(pc2, index);
539
 
    return JS_TRUE;
540
 
}
541
 
 
542
 
static JSBool
543
 
BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg)
544
 
{
545
 
    jsbytecode *pc, *end;
546
 
    JSOp op;
547
 
    const JSCodeSpec *cs;
548
 
    ptrdiff_t len, off;
549
 
 
550
 
    pc = CG_BASE(cg) + cg->spanDepTodo;
551
 
    end = CG_NEXT(cg);
552
 
    while (pc < end) {
553
 
        op = (JSOp)*pc;
554
 
        cs = &js_CodeSpec[op];
555
 
        len = (ptrdiff_t)cs->length;
556
 
 
557
 
        switch (cs->format & JOF_TYPEMASK) {
558
 
          case JOF_JUMP:
559
 
            off = GET_JUMP_OFFSET(pc);
560
 
            if (!AddSpanDep(cx, cg, pc, pc, off))
561
 
                return JS_FALSE;
562
 
            break;
563
 
 
564
 
          case JOF_TABLESWITCH:
565
 
          {
566
 
            jsbytecode *pc2;
567
 
            jsint i, low, high;
568
 
 
569
 
            pc2 = pc;
570
 
            off = GET_JUMP_OFFSET(pc2);
571
 
            if (!AddSpanDep(cx, cg, pc, pc2, off))
572
 
                return JS_FALSE;
573
 
            pc2 += JUMP_OFFSET_LEN;
574
 
            low = GET_JUMP_OFFSET(pc2);
575
 
            pc2 += JUMP_OFFSET_LEN;
576
 
            high = GET_JUMP_OFFSET(pc2);
577
 
            pc2 += JUMP_OFFSET_LEN;
578
 
            for (i = low; i <= high; i++) {
579
 
                off = GET_JUMP_OFFSET(pc2);
580
 
                if (!AddSpanDep(cx, cg, pc, pc2, off))
581
 
                    return JS_FALSE;
582
 
                pc2 += JUMP_OFFSET_LEN;
583
 
            }
584
 
            len = 1 + pc2 - pc;
585
 
            break;
586
 
          }
587
 
 
588
 
          case JOF_LOOKUPSWITCH:
589
 
          {
590
 
            jsbytecode *pc2;
591
 
            jsint npairs;
592
 
 
593
 
            pc2 = pc;
594
 
            off = GET_JUMP_OFFSET(pc2);
595
 
            if (!AddSpanDep(cx, cg, pc, pc2, off))
596
 
                return JS_FALSE;
597
 
            pc2 += JUMP_OFFSET_LEN;
598
 
            npairs = (jsint) GET_ATOM_INDEX(pc2);
599
 
            pc2 += ATOM_INDEX_LEN;
600
 
            while (npairs) {
601
 
                pc2 += ATOM_INDEX_LEN;
602
 
                off = GET_JUMP_OFFSET(pc2);
603
 
                if (!AddSpanDep(cx, cg, pc, pc2, off))
604
 
                    return JS_FALSE;
605
 
                pc2 += JUMP_OFFSET_LEN;
606
 
                npairs--;
607
 
            }
608
 
            len = 1 + pc2 - pc;
609
 
            break;
610
 
          }
611
 
        }
612
 
 
613
 
        JS_ASSERT(len > 0);
614
 
        pc += len;
615
 
    }
616
 
 
617
 
    return JS_TRUE;
618
 
}
619
 
 
620
 
static JSSpanDep *
621
 
GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc)
622
 
{
623
 
    uintN index;
624
 
    ptrdiff_t offset;
625
 
    int lo, hi, mid;
626
 
    JSSpanDep *sd;
627
 
 
628
 
    index = GET_SPANDEP_INDEX(pc);
629
 
    if (index != SPANDEP_INDEX_HUGE)
630
 
        return cg->spanDeps + index;
631
 
 
632
 
    offset = PTRDIFF(pc, CG_BASE(cg), jsbytecode);
633
 
    lo = 0;
634
 
    hi = cg->numSpanDeps - 1;
635
 
    while (lo <= hi) {
636
 
        mid = (lo + hi) / 2;
637
 
        sd = cg->spanDeps + mid;
638
 
        if (sd->before == offset)
639
 
            return sd;
640
 
        if (sd->before < offset)
641
 
            lo = mid + 1;
642
 
        else
643
 
            hi = mid - 1;
644
 
    }
645
 
 
646
 
    JS_ASSERT(0);
647
 
    return NULL;
648
 
}
649
 
 
650
 
static JSBool
651
 
SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
652
 
                  ptrdiff_t delta)
653
 
{
654
 
    JSSpanDep *sd;
655
 
 
656
 
    JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
657
 
    if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) {
658
 
        SET_JUMP_OFFSET(pc, delta);
659
 
        return JS_TRUE;
660
 
    }
661
 
 
662
 
    if (delta > BPDELTA_MAX) {
663
 
        ReportStatementTooLarge(cx, cg);
664
 
        return JS_FALSE;
665
 
    }
666
 
 
667
 
    if (!cg->spanDeps && !BuildSpanDepTable(cx, cg))
668
 
        return JS_FALSE;
669
 
 
670
 
    sd = GetSpanDep(cg, pc);
671
 
    JS_ASSERT(SD_GET_BPDELTA(sd) == 0);
672
 
    SD_SET_BPDELTA(sd, delta);
673
 
    return JS_TRUE;
674
 
}
675
 
 
676
 
static void
677
 
UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta)
678
 
{
679
 
    if (jt->offset > pivot) {
680
 
        jt->offset += delta;
681
 
        if (jt->kids[JT_LEFT])
682
 
            UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta);
683
 
    }
684
 
    if (jt->kids[JT_RIGHT])
685
 
        UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta);
686
 
}
687
 
 
688
 
static JSSpanDep *
689
 
FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo,
690
 
                   JSSpanDep *guard)
691
 
{
692
 
    int num, hi, mid;
693
 
    JSSpanDep *sdbase, *sd;
694
 
 
695
 
    num = cg->numSpanDeps;
696
 
    JS_ASSERT(num > 0);
697
 
    hi = num - 1;
698
 
    sdbase = cg->spanDeps;
699
 
    while (lo <= hi) {
700
 
        mid = (lo + hi) / 2;
701
 
        sd = sdbase + mid;
702
 
        if (sd->before == offset)
703
 
            return sd;
704
 
        if (sd->before < offset)
705
 
            lo = mid + 1;
706
 
        else
707
 
            hi = mid - 1;
708
 
    }
709
 
    if (lo == num)
710
 
        return guard;
711
 
    sd = sdbase + lo;
712
 
    JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset));
713
 
    return sd;
714
 
}
715
 
 
716
 
static void
717
 
FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt)
718
 
{
719
 
    if (jt->kids[JT_LEFT])
720
 
        FreeJumpTargets(cg, jt->kids[JT_LEFT]);
721
 
    if (jt->kids[JT_RIGHT])
722
 
        FreeJumpTargets(cg, jt->kids[JT_RIGHT]);
723
 
    jt->kids[JT_LEFT] = cg->jtFreeList;
724
 
    cg->jtFreeList = jt;
725
 
}
726
 
 
727
 
static JSBool
728
 
OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
729
 
{
730
 
    jsbytecode *pc, *oldpc, *base, *limit, *next;
731
 
    JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard;
732
 
    ptrdiff_t offset, growth, delta, top, pivot, span, length, target;
733
 
    JSBool done;
734
 
    JSOp op;
735
 
    uint32 type;
736
 
    size_t size, incr;
737
 
    jssrcnote *sn, *snlimit;
738
 
    JSSrcNoteSpec *spec;
739
 
    uintN i, n, noteIndex;
740
 
    JSTryNote *tn, *tnlimit;
741
 
#ifdef DEBUG_brendan
742
 
    int passes = 0;
743
 
#endif
744
 
 
745
 
    base = CG_BASE(cg);
746
 
    sdbase = cg->spanDeps;
747
 
    sdlimit = sdbase + cg->numSpanDeps;
748
 
    offset = CG_OFFSET(cg);
749
 
    growth = 0;
750
 
 
751
 
    do {
752
 
        done = JS_TRUE;
753
 
        delta = 0;
754
 
        top = pivot = -1;
755
 
        sdtop = NULL;
756
 
        pc = NULL;
757
 
        op = JSOP_NOP;
758
 
        type = 0;
759
 
#ifdef DEBUG_brendan
760
 
        passes++;
761
 
#endif
762
 
 
763
 
        for (sd = sdbase; sd < sdlimit; sd++) {
764
 
            JS_ASSERT(JT_HAS_TAG(sd->target));
765
 
            sd->offset += delta;
766
 
 
767
 
            if (sd->top != top) {
768
 
                sdtop = sd;
769
 
                top = sd->top;
770
 
                JS_ASSERT(top == sd->before);
771
 
                pivot = sd->offset;
772
 
                pc = base + top;
773
 
                op = (JSOp) *pc;
774
 
                type = (js_CodeSpec[op].format & JOF_TYPEMASK);
775
 
                if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
776
 
                    /*
777
 
                     * We already extended all the jump offset operands for
778
 
                     * the opcode at sd->top.  Jumps and branches have only
779
 
                     * one jump offset operand, but switches have many, all
780
 
                     * of which are adjacent in cg->spanDeps.
781
 
                     */
782
 
                    continue;
783
 
                }
784
 
 
785
 
                JS_ASSERT(type == JOF_JUMP ||
786
 
                          type == JOF_TABLESWITCH ||
787
 
                          type == JOF_LOOKUPSWITCH);
788
 
            }
789
 
 
790
 
            if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) {
791
 
                span = SD_SPAN(sd, pivot);
792
 
                if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) {
793
 
                    ptrdiff_t deltaFromTop = 0;
794
 
 
795
 
                    done = JS_FALSE;
796
 
 
797
 
                    switch (op) {
798
 
                      case JSOP_GOTO:         op = JSOP_GOTOX; break;
799
 
                      case JSOP_IFEQ:         op = JSOP_IFEQX; break;
800
 
                      case JSOP_IFNE:         op = JSOP_IFNEX; break;
801
 
                      case JSOP_OR:           op = JSOP_ORX; break;
802
 
                      case JSOP_AND:          op = JSOP_ANDX; break;
803
 
                      case JSOP_GOSUB:        op = JSOP_GOSUBX; break;
804
 
                      case JSOP_CASE:         op = JSOP_CASEX; break;
805
 
                      case JSOP_DEFAULT:      op = JSOP_DEFAULTX; break;
806
 
                      case JSOP_TABLESWITCH:  op = JSOP_TABLESWITCHX; break;
807
 
                      case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break;
808
 
                      default:
809
 
                        ReportStatementTooLarge(cx, cg);
810
 
                        return JS_FALSE;
811
 
                    }
812
 
                    *pc = (jsbytecode) op;
813
 
 
814
 
                    for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) {
815
 
                        if (sd2 <= sd) {
816
 
                            /*
817
 
                             * sd2->offset already includes delta as it stood
818
 
                             * before we entered this loop, but it must also
819
 
                             * include the delta relative to top due to all the
820
 
                             * extended jump offset immediates for the opcode
821
 
                             * starting at top, which we extend in this loop.
822
 
                             *
823
 
                             * If there is only one extended jump offset, then
824
 
                             * sd2->offset won't change and this for loop will
825
 
                             * iterate once only.
826
 
                             */
827
 
                            sd2->offset += deltaFromTop;
828
 
                            deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN;
829
 
                        } else {
830
 
                            /*
831
 
                             * sd2 comes after sd, and won't be revisited by
832
 
                             * the outer for loop, so we have to increase its
833
 
                             * offset by delta, not merely by deltaFromTop.
834
 
                             */
835
 
                            sd2->offset += delta;
836
 
                        }
837
 
 
838
 
                        delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN;
839
 
                        UpdateJumpTargets(cg->jumpTargets, sd2->offset,
840
 
                                          JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN);
841
 
                    }
842
 
                    sd = sd2 - 1;
843
 
                }
844
 
            }
845
 
        }
846
 
 
847
 
        growth += delta;
848
 
    } while (!done);
849
 
 
850
 
    if (growth) {
851
 
#ifdef DEBUG_brendan
852
 
        printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n",
853
 
               cg->filename ? cg->filename : "stdin", cg->firstLine,
854
 
               growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps,
855
 
               passes, offset + growth, offset, growth);
856
 
#endif
857
 
 
858
 
        /*
859
 
         * Ensure that we have room for the extended jumps, but don't round up
860
 
         * to a power of two -- we're done generating code, so we cut to fit.
861
 
         */
862
 
        limit = CG_LIMIT(cg);
863
 
        length = offset + growth;
864
 
        next = base + length;
865
 
        if (next > limit) {
866
 
            JS_ASSERT(length > BYTECODE_CHUNK);
867
 
            size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
868
 
            incr = BYTECODE_SIZE(length) - size;
869
 
            JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
870
 
            if (!base) {
871
 
                JS_ReportOutOfMemory(cx);
872
 
                return JS_FALSE;
873
 
            }
874
 
            CG_BASE(cg) = base;
875
 
            CG_LIMIT(cg) = next = base + length;
876
 
        }
877
 
        CG_NEXT(cg) = next;
878
 
 
879
 
        /*
880
 
         * Set up a fake span dependency record to guard the end of the code
881
 
         * being generated.  This guard record is returned as a fencepost by
882
 
         * FindNearestSpanDep if there is no real spandep at or above a given
883
 
         * unextended code offset.
884
 
         */
885
 
        guard.top = -1;
886
 
        guard.offset = offset + growth;
887
 
        guard.before = offset;
888
 
        guard.target = NULL;
889
 
    }
890
 
 
891
 
    /*
892
 
     * Now work backwards through the span dependencies, copying chunks of
893
 
     * bytecode between each extended jump toward the end of the grown code
894
 
     * space, and restoring immediate offset operands for all jump bytecodes.
895
 
     * The first chunk of bytecodes, starting at base and ending at the first
896
 
     * extended jump offset (NB: this chunk includes the operation bytecode
897
 
     * just before that immediate jump offset), doesn't need to be copied.
898
 
     */
899
 
    JS_ASSERT(sd == sdlimit);
900
 
    top = -1;
901
 
    while (--sd >= sdbase) {
902
 
        if (sd->top != top) {
903
 
            top = sd->top;
904
 
            op = (JSOp) base[top];
905
 
            type = (js_CodeSpec[op].format & JOF_TYPEMASK);
906
 
 
907
 
            for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--)
908
 
                continue;
909
 
            sd2++;
910
 
            pivot = sd2->offset;
911
 
            JS_ASSERT(top == sd2->before);
912
 
        }
913
 
 
914
 
        oldpc = base + sd->before;
915
 
        span = SD_SPAN(sd, pivot);
916
 
 
917
 
        /*
918
 
         * If this jump didn't need to be extended, restore its span immediate
919
 
         * offset operand now, overwriting the index of sd within cg->spanDeps
920
 
         * that was stored temporarily after *pc when BuildSpanDepTable ran.
921
 
         *
922
 
         * Note that span might fit in 16 bits even for an extended jump op,
923
 
         * if the op has multiple span operands, not all of which overflowed
924
 
         * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in
925
 
         * range for a short jump, but others are not).
926
 
         */
927
 
        if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) {
928
 
            JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX);
929
 
            SET_JUMP_OFFSET(oldpc, span);
930
 
            continue;
931
 
        }
932
 
 
933
 
        /*
934
 
         * Set up parameters needed to copy the next run of bytecode starting
935
 
         * at offset (which is a cursor into the unextended, original bytecode
936
 
         * vector), down to sd->before (a cursor of the same scale as offset,
937
 
         * it's the index of the original jump pc).  Reuse delta to count the
938
 
         * nominal number of bytes to copy.
939
 
         */
940
 
        pc = base + sd->offset;
941
 
        delta = offset - sd->before;
942
 
        JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
943
 
 
944
 
        /*
945
 
         * Don't bother copying the jump offset we're about to reset, but do
946
 
         * copy the bytecode at oldpc (which comes just before its immediate
947
 
         * jump offset operand), on the next iteration through the loop, by
948
 
         * including it in offset's new value.
949
 
         */
950
 
        offset = sd->before + 1;
951
 
        size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN));
952
 
        if (size) {
953
 
            memmove(pc + 1 + JUMPX_OFFSET_LEN,
954
 
                    oldpc + 1 + JUMP_OFFSET_LEN,
955
 
                    size);
956
 
        }
957
 
 
958
 
        SET_JUMPX_OFFSET(pc, span);
959
 
    }
960
 
 
961
 
    if (growth) {
962
 
        /*
963
 
         * Fix source note deltas.  Don't hardwire the delta fixup adjustment,
964
 
         * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN
965
 
         * at each sd that moved.  The future may bring different offset sizes
966
 
         * for span-dependent instruction operands.  However, we fix only main
967
 
         * notes here, not prolog notes -- we know that prolog opcodes are not
968
 
         * span-dependent, and aren't likely ever to be.
969
 
         */
970
 
        offset = growth = 0;
971
 
        sd = sdbase;
972
 
        for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount;
973
 
             sn < snlimit;
974
 
             sn = SN_NEXT(sn)) {
975
 
            /*
976
 
             * Recall that the offset of a given note includes its delta, and
977
 
             * tells the offset of the annotated bytecode from the main entry
978
 
             * point of the script.
979
 
             */
980
 
            offset += SN_DELTA(sn);
981
 
            while (sd < sdlimit && sd->before < offset) {
982
 
                /*
983
 
                 * To compute the delta to add to sn, we need to look at the
984
 
                 * spandep after sd, whose offset - (before + growth) tells by
985
 
                 * how many bytes sd's instruction grew.
986
 
                 */
987
 
                sd2 = sd + 1;
988
 
                if (sd2 == sdlimit)
989
 
                    sd2 = &guard;
990
 
                delta = sd2->offset - (sd2->before + growth);
991
 
                if (delta > 0) {
992
 
                    JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN);
993
 
                    sn = js_AddToSrcNoteDelta(cx, cg, sn, delta);
994
 
                    if (!sn)
995
 
                        return JS_FALSE;
996
 
                    snlimit = cg->main.notes + cg->main.noteCount;
997
 
                    growth += delta;
998
 
                }
999
 
                sd++;
1000
 
            }
1001
 
 
1002
 
            /*
1003
 
             * If sn has span-dependent offset operands, check whether each
1004
 
             * covers further span-dependencies, and increase those operands
1005
 
             * accordingly.  Some source notes measure offset not from the
1006
 
             * annotated pc, but from that pc plus some small bias.  NB: we
1007
 
             * assume that spec->offsetBias can't itself span span-dependent
1008
 
             * instructions!
1009
 
             */
1010
 
            spec = &js_SrcNoteSpec[SN_TYPE(sn)];
1011
 
            if (spec->isSpanDep) {
1012
 
                pivot = offset + spec->offsetBias;
1013
 
                n = spec->arity;
1014
 
                for (i = 0; i < n; i++) {
1015
 
                    span = js_GetSrcNoteOffset(sn, i);
1016
 
                    if (span == 0)
1017
 
                        continue;
1018
 
                    target = pivot + span * spec->isSpanDep;
1019
 
                    sd2 = FindNearestSpanDep(cg, target,
1020
 
                                             (target >= pivot)
1021
 
                                             ? sd - sdbase
1022
 
                                             : 0,
1023
 
                                             &guard);
1024
 
 
1025
 
                    /*
1026
 
                     * Increase target by sd2's before-vs-after offset delta,
1027
 
                     * which is absolute (i.e., relative to start of script,
1028
 
                     * as is target).  Recompute the span by subtracting its
1029
 
                     * adjusted pivot from target.
1030
 
                     */
1031
 
                    target += sd2->offset - sd2->before;
1032
 
                    span = target - (pivot + growth);
1033
 
                    span *= spec->isSpanDep;
1034
 
                    noteIndex = sn - cg->main.notes;
1035
 
                    if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span))
1036
 
                        return JS_FALSE;
1037
 
                    sn = cg->main.notes + noteIndex;
1038
 
                    snlimit = cg->main.notes + cg->main.noteCount;
1039
 
                }
1040
 
            }
1041
 
        }
1042
 
        cg->main.lastNoteOffset += growth;
1043
 
 
1044
 
        /*
1045
 
         * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's
1046
 
         * not clear how we can beat that).
1047
 
         */
1048
 
        for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) {
1049
 
            /*
1050
 
             * First, look for the nearest span dependency at/above tn->start.
1051
 
             * There may not be any such spandep, in which case the guard will
1052
 
             * be returned.
1053
 
             */
1054
 
            offset = tn->start;
1055
 
            sd = FindNearestSpanDep(cg, offset, 0, &guard);
1056
 
            delta = sd->offset - sd->before;
1057
 
            tn->start = offset + delta;
1058
 
 
1059
 
            /*
1060
 
             * Next, find the nearest spandep at/above tn->start + tn->length.
1061
 
             * Use its delta minus tn->start's delta to increase tn->length.
1062
 
             */
1063
 
            length = tn->length;
1064
 
            sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard);
1065
 
            if (sd2 != sd)
1066
 
                tn->length = length + sd2->offset - sd2->before - delta;
1067
 
 
1068
 
            /*
1069
 
             * Finally, adjust tn->catchStart upward only if it is non-zero,
1070
 
             * and provided there are spandeps below it that grew.
1071
 
             */
1072
 
            offset = tn->catchStart;
1073
 
            if (offset != 0) {
1074
 
                sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard);
1075
 
                tn->catchStart = offset + sd->offset - sd->before;
1076
 
            }
1077
 
        }
1078
 
    }
1079
 
 
1080
 
#ifdef DEBUG_brendan
1081
 
  {
1082
 
    uintN bigspans = 0;
1083
 
    top = -1;
1084
 
    for (sd = sdbase; sd < sdlimit; sd++) {
1085
 
        offset = sd->offset;
1086
 
 
1087
 
        /* NB: sd->top cursors into the original, unextended bytecode vector. */
1088
 
        if (sd->top != top) {
1089
 
            JS_ASSERT(top == -1 ||
1090
 
                      !JOF_TYPE_IS_EXTENDED_JUMP(type) ||
1091
 
                      bigspans != 0);
1092
 
            bigspans = 0;
1093
 
            top = sd->top;
1094
 
            JS_ASSERT(top == sd->before);
1095
 
            op = (JSOp) base[offset];
1096
 
            type = (js_CodeSpec[op].format & JOF_TYPEMASK);
1097
 
            JS_ASSERT(type == JOF_JUMP ||
1098
 
                      type == JOF_JUMPX ||
1099
 
                      type == JOF_TABLESWITCH ||
1100
 
                      type == JOF_TABLESWITCHX ||
1101
 
                      type == JOF_LOOKUPSWITCH ||
1102
 
                      type == JOF_LOOKUPSWITCHX);
1103
 
            pivot = offset;
1104
 
        }
1105
 
 
1106
 
        pc = base + offset;
1107
 
        if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
1108
 
            span = GET_JUMPX_OFFSET(pc);
1109
 
            if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) {
1110
 
                bigspans++;
1111
 
            } else {
1112
 
                JS_ASSERT(type == JOF_TABLESWITCHX ||
1113
 
                          type == JOF_LOOKUPSWITCHX);
1114
 
            }
1115
 
        } else {
1116
 
            span = GET_JUMP_OFFSET(pc);
1117
 
        }
1118
 
        JS_ASSERT(SD_SPAN(sd, pivot) == span);
1119
 
    }
1120
 
    JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0);
1121
 
  }
1122
 
#endif
1123
 
 
1124
 
    /*
1125
 
     * Reset so we optimize at most once -- cg may be used for further code
1126
 
     * generation of successive, independent, top-level statements.  No jump
1127
 
     * can span top-level statements, because JS lacks goto.
1128
 
     */
1129
 
    size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps)));
1130
 
    JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps,
1131
 
                           JS_MAX(size, SPANDEPS_SIZE_MIN));
1132
 
    cg->spanDeps = NULL;
1133
 
    FreeJumpTargets(cg, cg->jumpTargets);
1134
 
    cg->jumpTargets = NULL;
1135
 
    cg->numSpanDeps = cg->numJumpTargets = 0;
1136
 
    cg->spanDepTodo = CG_OFFSET(cg);
1137
 
    return JS_TRUE;
1138
 
}
1139
 
 
1140
 
static JSBool
1141
 
EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off)
1142
 
{
1143
 
    JSBool extend;
1144
 
    ptrdiff_t jmp;
1145
 
    jsbytecode *pc;
1146
 
 
1147
 
    extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off;
1148
 
    if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg))
1149
 
        return JS_FALSE;
1150
 
 
1151
 
    jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off));
1152
 
    if (jmp >= 0 && (extend || cg->spanDeps)) {
1153
 
        pc = CG_CODE(cg, jmp);
1154
 
        if (!AddSpanDep(cx, cg, pc, pc, off))
1155
 
            return JS_FALSE;
1156
 
    }
1157
 
    return jmp;
1158
 
}
1159
 
 
1160
 
static ptrdiff_t
1161
 
GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc)
1162
 
{
1163
 
    JSSpanDep *sd;
1164
 
    JSJumpTarget *jt;
1165
 
    ptrdiff_t top;
1166
 
 
1167
 
    if (!cg->spanDeps)
1168
 
        return GET_JUMP_OFFSET(pc);
1169
 
 
1170
 
    sd = GetSpanDep(cg, pc);
1171
 
    jt = sd->target;
1172
 
    if (!JT_HAS_TAG(jt))
1173
 
        return JT_TO_BPDELTA(jt);
1174
 
 
1175
 
    top = sd->top;
1176
 
    while (--sd >= cg->spanDeps && sd->top == top)
1177
 
        continue;
1178
 
    sd++;
1179
 
    return JT_CLR_TAG(jt)->offset - sd->offset;
1180
 
}
1181
 
 
1182
 
JSBool
1183
 
js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
1184
 
                 ptrdiff_t off)
1185
 
{
1186
 
    if (!cg->spanDeps) {
1187
 
        if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) {
1188
 
            SET_JUMP_OFFSET(pc, off);
1189
 
            return JS_TRUE;
1190
 
        }
1191
 
 
1192
 
        if (!BuildSpanDepTable(cx, cg))
1193
 
            return JS_FALSE;
1194
 
    }
1195
 
 
1196
 
    return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off);
1197
 
}
1198
 
 
1199
 
JSBool
1200
 
js_InStatement(JSTreeContext *tc, JSStmtType type)
1201
 
{
1202
 
    JSStmtInfo *stmt;
1203
 
 
1204
 
    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1205
 
        if (stmt->type == type)
1206
 
            return JS_TRUE;
1207
 
    }
1208
 
    return JS_FALSE;
1209
 
}
1210
 
 
1211
 
JSBool
1212
 
js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp)
1213
 
{
1214
 
    JSStmtInfo *stmt;
1215
 
    JSObject *obj;
1216
 
    JSScope *scope;
1217
 
 
1218
 
    *loopyp = JS_FALSE;
1219
 
    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1220
 
        if (stmt->type == STMT_WITH)
1221
 
            return JS_FALSE;
1222
 
        if (STMT_IS_LOOP(stmt)) {
1223
 
            *loopyp = JS_TRUE;
1224
 
            continue;
1225
 
        }
1226
 
        if (stmt->flags & SIF_SCOPE) {
1227
 
            obj = ATOM_TO_OBJECT(stmt->atom);
1228
 
            JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
1229
 
            scope = OBJ_SCOPE(obj);
1230
 
            if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)))
1231
 
                return JS_FALSE;
1232
 
        }
1233
 
    }
1234
 
    return JS_TRUE;
1235
 
}
1236
 
 
1237
 
void
1238
 
js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
1239
 
                 ptrdiff_t top)
1240
 
{
1241
 
    stmt->type = type;
1242
 
    stmt->flags = 0;
1243
 
    SET_STATEMENT_TOP(stmt, top);
1244
 
    stmt->atom = NULL;
1245
 
    stmt->down = tc->topStmt;
1246
 
    tc->topStmt = stmt;
1247
 
    if (STMT_LINKS_SCOPE(stmt)) {
1248
 
        stmt->downScope = tc->topScopeStmt;
1249
 
        tc->topScopeStmt = stmt;
1250
 
    } else {
1251
 
        stmt->downScope = NULL;
1252
 
    }
1253
 
}
1254
 
 
1255
 
void
1256
 
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom,
1257
 
                  ptrdiff_t top)
1258
 
{
1259
 
    JSObject *blockObj;
1260
 
 
1261
 
    js_PushStatement(tc, stmt, STMT_BLOCK, top);
1262
 
    stmt->flags |= SIF_SCOPE;
1263
 
    blockObj = ATOM_TO_OBJECT(blockAtom);
1264
 
    blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
1265
 
    stmt->downScope = tc->topScopeStmt;
1266
 
    tc->topScopeStmt = stmt;
1267
 
    tc->blockChain = blockObj;
1268
 
    stmt->atom = blockAtom;
1269
 
}
1270
 
 
1271
 
/*
1272
 
 * Emit a backpatch op with offset pointing to the previous jump of this type,
1273
 
 * so that we can walk back up the chain fixing up the op and jump offset.
1274
 
 */
1275
 
static ptrdiff_t
1276
 
EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp)
1277
 
{
1278
 
    ptrdiff_t offset, delta;
1279
 
 
1280
 
    offset = CG_OFFSET(cg);
1281
 
    delta = offset - *lastp;
1282
 
    *lastp = offset;
1283
 
    JS_ASSERT(delta > 0);
1284
 
    return EmitJump(cx, cg, op, delta);
1285
 
}
1286
 
 
1287
 
/*
1288
 
 * Macro to emit a bytecode followed by a uint16 immediate operand stored in
1289
 
 * big-endian order, used for arg and var numbers as well as for atomIndexes.
1290
 
 * NB: We use cx and cg from our caller's lexical environment, and return
1291
 
 * false on error.
1292
 
 */
1293
 
#define EMIT_UINT16_IMM_OP(op, i)                                             \
1294
 
    JS_BEGIN_MACRO                                                            \
1295
 
        if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0)             \
1296
 
            return JS_FALSE;                                                  \
1297
 
    JS_END_MACRO
1298
 
 
1299
 
/* Emit additional bytecode(s) for non-local jumps. */
1300
 
static JSBool
1301
 
EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
1302
 
                      JSOp *returnop)
1303
 
{
1304
 
    intN depth;
1305
 
    JSStmtInfo *stmt;
1306
 
    ptrdiff_t jmp;
1307
 
 
1308
 
    /*
1309
 
     * Return from within a try block that has a finally clause must be split
1310
 
     * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval;
1311
 
     * and JSOP_RETRVAL, which makes control flow go back to the caller, who
1312
 
     * picks up fp->rval as usual.  Otherwise, the stack will be unbalanced
1313
 
     * when executing the finally clause.
1314
 
     *
1315
 
     * We mutate *returnop once only if we find an enclosing try-block (viz,
1316
 
     * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one
1317
 
     * or more JSOP_GOSUBs and other fixup opcodes emitted by this function.
1318
 
     * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop.
1319
 
     * The fixup opcodes and gosubs must interleave in the proper order, from
1320
 
     * inner statement to outer, so that finally clauses run at the correct
1321
 
     * stack depth.
1322
 
     */
1323
 
    if (returnop) {
1324
 
        JS_ASSERT(*returnop == JSOP_RETURN);
1325
 
        for (stmt = cg->treeContext.topStmt; stmt != toStmt;
1326
 
             stmt = stmt->down) {
1327
 
            if (stmt->type == STMT_FINALLY ||
1328
 
                ((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) &&
1329
 
                 STMT_MAYBE_SCOPE(stmt))) {
1330
 
                if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
1331
 
                    return JS_FALSE;
1332
 
                *returnop = JSOP_RETRVAL;
1333
 
                break;
1334
 
            }
1335
 
        }
1336
 
 
1337
 
        /*
1338
 
         * If there are no try-with-finally blocks open around this return
1339
 
         * statement, we can generate a return forthwith and skip generating
1340
 
         * any fixup code.
1341
 
         */
1342
 
        if (*returnop == JSOP_RETURN)
1343
 
            return JS_TRUE;
1344
 
    }
1345
 
 
1346
 
    /*
1347
 
     * The non-local jump fixup we emit will unbalance cg->stackDepth, because
1348
 
     * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
1349
 
     * end of a with statement, so we save cg->stackDepth here and restore it
1350
 
     * just before a successful return.
1351
 
     */
1352
 
    depth = cg->stackDepth;
1353
 
    for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
1354
 
        switch (stmt->type) {
1355
 
          case STMT_FINALLY:
1356
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1357
 
                return JS_FALSE;
1358
 
            jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt));
1359
 
            if (jmp < 0)
1360
 
                return JS_FALSE;
1361
 
            break;
1362
 
 
1363
 
          case STMT_WITH:
1364
 
            /* There's a With object on the stack that we need to pop. */
1365
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1366
 
                return JS_FALSE;
1367
 
            if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
1368
 
                return JS_FALSE;
1369
 
            break;
1370
 
 
1371
 
          case STMT_FOR_IN_LOOP:
1372
 
            /*
1373
 
             * The iterator and the object being iterated need to be popped.
1374
 
             */
1375
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1376
 
                return JS_FALSE;
1377
 
            if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
1378
 
                return JS_FALSE;
1379
 
            break;
1380
 
 
1381
 
          case STMT_SUBROUTINE:
1382
 
            /*
1383
 
             * There's a [exception or hole, retsub pc-index] pair on the
1384
 
             * stack that we need to pop.
1385
 
             */
1386
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1387
 
                return JS_FALSE;
1388
 
            if (js_Emit1(cx, cg, JSOP_POP2) < 0)
1389
 
                return JS_FALSE;
1390
 
            break;
1391
 
 
1392
 
          default:;
1393
 
        }
1394
 
 
1395
 
        if (stmt->flags & SIF_SCOPE) {
1396
 
            uintN i;
1397
 
 
1398
 
            /* There is a Block object with locals on the stack to pop. */
1399
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1400
 
                return JS_FALSE;
1401
 
            i = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(stmt->atom));
1402
 
            EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i);
1403
 
        }
1404
 
    }
1405
 
 
1406
 
    cg->stackDepth = depth;
1407
 
    return JS_TRUE;
1408
 
}
1409
 
 
1410
 
static ptrdiff_t
1411
 
EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
1412
 
         ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType)
1413
 
{
1414
 
    intN index;
1415
 
 
1416
 
    if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
1417
 
        return -1;
1418
 
 
1419
 
    if (label)
1420
 
        index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label));
1421
 
    else if (noteType != SRC_NULL)
1422
 
        index = js_NewSrcNote(cx, cg, noteType);
1423
 
    else
1424
 
        index = 0;
1425
 
    if (index < 0)
1426
 
        return -1;
1427
 
 
1428
 
    return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp);
1429
 
}
1430
 
 
1431
 
static JSBool
1432
 
BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
1433
 
          jsbytecode *target, jsbytecode op)
1434
 
{
1435
 
    jsbytecode *pc, *stop;
1436
 
    ptrdiff_t delta, span;
1437
 
 
1438
 
    pc = CG_CODE(cg, last);
1439
 
    stop = CG_CODE(cg, -1);
1440
 
    while (pc != stop) {
1441
 
        delta = GetJumpOffset(cg, pc);
1442
 
        span = PTRDIFF(target, pc, jsbytecode);
1443
 
        CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span);
1444
 
 
1445
 
        /*
1446
 
         * Set *pc after jump offset in case bpdelta didn't overflow, but span
1447
 
         * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable
1448
 
         * and need to see the JSOP_BACKPATCH* op at *pc).
1449
 
         */
1450
 
        *pc = op;
1451
 
        pc -= delta;
1452
 
    }
1453
 
    return JS_TRUE;
1454
 
}
1455
 
 
1456
 
void
1457
 
js_PopStatement(JSTreeContext *tc)
1458
 
{
1459
 
    JSStmtInfo *stmt;
1460
 
    JSObject *blockObj;
1461
 
 
1462
 
    stmt = tc->topStmt;
1463
 
    tc->topStmt = stmt->down;
1464
 
    if (STMT_LINKS_SCOPE(stmt)) {
1465
 
        tc->topScopeStmt = stmt->downScope;
1466
 
        if (stmt->flags & SIF_SCOPE) {
1467
 
            blockObj = ATOM_TO_OBJECT(stmt->atom);
1468
 
            tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]);
1469
 
        }
1470
 
    }
1471
 
}
1472
 
 
1473
 
JSBool
1474
 
js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
1475
 
{
1476
 
    JSStmtInfo *stmt;
1477
 
 
1478
 
    stmt = cg->treeContext.topStmt;
1479
 
    if (!STMT_IS_TRYING(stmt) &&
1480
 
        (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
1481
 
         !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update),
1482
 
                    JSOP_GOTO))) {
1483
 
        return JS_FALSE;
1484
 
    }
1485
 
    js_PopStatement(&cg->treeContext);
1486
 
    return JS_TRUE;
1487
 
}
1488
 
 
1489
 
JSBool
1490
 
js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
1491
 
                             JSParseNode *pn)
1492
 
{
1493
 
    jsdouble dval;
1494
 
    jsint ival;
1495
 
    JSAtom *valueAtom;
1496
 
    JSAtomListElement *ale;
1497
 
 
1498
 
    /* XXX just do numbers for now */
1499
 
    if (pn->pn_type == TOK_NUMBER) {
1500
 
        dval = pn->pn_dval;
1501
 
        valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival))
1502
 
                    ? js_AtomizeInt(cx, ival, 0)
1503
 
                    : js_AtomizeDouble(cx, dval, 0);
1504
 
        if (!valueAtom)
1505
 
            return JS_FALSE;
1506
 
        ale = js_IndexAtom(cx, atom, &cg->constList);
1507
 
        if (!ale)
1508
 
            return JS_FALSE;
1509
 
        ALE_SET_VALUE(ale, ATOM_KEY(valueAtom));
1510
 
    }
1511
 
    return JS_TRUE;
1512
 
}
1513
 
 
1514
 
JSStmtInfo *
1515
 
js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl)
1516
 
{
1517
 
    JSStmtInfo *stmt;
1518
 
    JSObject *obj;
1519
 
    JSScope *scope;
1520
 
    JSScopeProperty *sprop;
1521
 
    jsval v;
1522
 
 
1523
 
    for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
1524
 
        if (stmt->type == STMT_WITH) {
1525
 
            /* Ignore with statements enclosing a single let declaration. */
1526
 
            if (letdecl)
1527
 
                continue;
1528
 
            break;
1529
 
        }
1530
 
 
1531
 
        /* Skip "maybe scope" statements that don't contain let bindings. */
1532
 
        if (!(stmt->flags & SIF_SCOPE))
1533
 
            continue;
1534
 
 
1535
 
        obj = ATOM_TO_OBJECT(stmt->atom);
1536
 
        JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
1537
 
        scope = OBJ_SCOPE(obj);
1538
 
        sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
1539
 
        if (sprop) {
1540
 
            JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1541
 
 
1542
 
            if (slotp) {
1543
 
                /*
1544
 
                 * Use LOCKED_OBJ_GET_SLOT since we know obj is single-
1545
 
                 * threaded and owned by this compiler activation.
1546
 
                 */
1547
 
                v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH);
1548
 
                JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0);
1549
 
                *slotp = JSVAL_TO_INT(v) + sprop->shortid;
1550
 
            }
1551
 
            return stmt;
1552
 
        }
1553
 
    }
1554
 
 
1555
 
    if (slotp)
1556
 
        *slotp = -1;
1557
 
    return stmt;
1558
 
}
1559
 
 
1560
 
JSBool
1561
 
js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
1562
 
                             jsval *vp)
1563
 
{
1564
 
    JSBool ok;
1565
 
    JSStackFrame *fp;
1566
 
    JSStmtInfo *stmt;
1567
 
    jsint slot;
1568
 
    JSAtomListElement *ale;
1569
 
    JSObject *obj, *pobj;
1570
 
    JSProperty *prop;
1571
 
    uintN attrs;
1572
 
 
1573
 
    /*
1574
 
     * fp chases cg down the stack, but only until we reach the outermost cg.
1575
 
     * This enables propagating consts from top-level into switch cases in a
1576
 
     * function compiled along with the top-level script.  All stack frames
1577
 
     * with matching code generators should be flagged with JSFRAME_COMPILING;
1578
 
     * we check sanity here.
1579
 
     */
1580
 
    *vp = JSVAL_VOID;
1581
 
    ok = JS_TRUE;
1582
 
    fp = cx->fp;
1583
 
    do {
1584
 
        JS_ASSERT(fp->flags & JSFRAME_COMPILING);
1585
 
 
1586
 
        obj = fp->varobj;
1587
 
        if (obj == fp->scopeChain) {
1588
 
            /* XXX this will need revising when 'let const' is added. */
1589
 
            stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE);
1590
 
            if (stmt)
1591
 
                return JS_TRUE;
1592
 
 
1593
 
            ATOM_LIST_SEARCH(ale, &cg->constList, atom);
1594
 
            if (ale) {
1595
 
                *vp = ALE_VALUE(ale);
1596
 
                return JS_TRUE;
1597
 
            }
1598
 
 
1599
 
            /*
1600
 
             * Try looking in the variable object for a direct property that
1601
 
             * is readonly and permanent.  We know such a property can't be
1602
 
             * shadowed by another property on obj's prototype chain, or a
1603
 
             * with object or catch variable; nor can prop's value be changed,
1604
 
             * nor can prop be deleted.
1605
 
             */
1606
 
            prop = NULL;
1607
 
            if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) {
1608
 
                ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
1609
 
                                             &pobj, &prop);
1610
 
                if (!ok)
1611
 
                    break;
1612
 
                if (prop) {
1613
 
#ifdef DEBUG
1614
 
                    JSScopeProperty *sprop = (JSScopeProperty *)prop;
1615
 
 
1616
 
                    /*
1617
 
                     * Any hidden property must be a formal arg or local var,
1618
 
                     * which will shadow a global const of the same name.
1619
 
                     */
1620
 
                    JS_ASSERT(sprop->getter == js_GetArgument ||
1621
 
                              sprop->getter == js_GetLocalVariable);
1622
 
#endif
1623
 
                    OBJ_DROP_PROPERTY(cx, pobj, prop);
1624
 
                    break;
1625
 
                }
1626
 
            }
1627
 
 
1628
 
            ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
1629
 
            if (ok) {
1630
 
                if (pobj == obj &&
1631
 
                    (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) {
1632
 
                    /*
1633
 
                     * We're compiling code that will be executed immediately,
1634
 
                     * not re-executed against a different scope chain and/or
1635
 
                     * variable object.  Therefore we can get constant values
1636
 
                     * from our variable object here.
1637
 
                     */
1638
 
                    ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop,
1639
 
                                            &attrs);
1640
 
                    if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)))
1641
 
                        ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp);
1642
 
                }
1643
 
                if (prop)
1644
 
                    OBJ_DROP_PROPERTY(cx, pobj, prop);
1645
 
            }
1646
 
            if (!ok || prop)
1647
 
                break;
1648
 
        }
1649
 
        fp = fp->down;
1650
 
    } while ((cg = cg->parent) != NULL);
1651
 
    return ok;
1652
 
}
1653
 
 
1654
 
/*
1655
 
 * Allocate an index invariant for all activations of the code being compiled
1656
 
 * in cg, that can be used to store and fetch a reference to a cloned RegExp
1657
 
 * object that shares the same JSRegExp private data created for the object
1658
 
 * literal in pn->pn_atom.  We need clones to hold lastIndex and other direct
1659
 
 * properties that should not be shared among threads sharing a precompiled
1660
 
 * function or script.
1661
 
 *
1662
 
 * If the code being compiled is function code, allocate a reserved slot in
1663
 
 * the cloned function object that shares its precompiled script with other
1664
 
 * cloned function objects and with the compiler-created clone-parent.  There
1665
 
 * are fun->nregexps such reserved slots in each function object cloned from
1666
 
 * fun->object.  NB: during compilation, funobj slots must never be allocated,
1667
 
 * because js_AllocSlot could hand out one of the slots that should be given
1668
 
 * to a regexp clone.
1669
 
 *
1670
 
 * If the code being compiled is global code, reserve the fp->vars slot at
1671
 
 * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least
1672
 
 * one more than this index.  For global code, fp->vars is parallel to the
1673
 
 * global script->atomMap.vector array, but possibly shorter for the common
1674
 
 * case (where var declarations and regexp literals cluster toward the front
1675
 
 * of the script or function body).
1676
 
 *
1677
 
 * Global variable name literals in script->atomMap have fast-global slot
1678
 
 * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array
1679
 
 * element.  The atomIndex for a regexp object literal thus also addresses an
1680
 
 * fp->vars element that is not used by any optimized global variable, so we
1681
 
 * use that GC-scanned element to keep the regexp object clone alive, as well
1682
 
 * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode.
1683
 
 *
1684
 
 * In no case can cx->fp->varobj be a Call object here, because that implies
1685
 
 * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL)
1686
 
 * is true, and js_GetToken will have already selected JSOP_OBJECT instead of
1687
 
 * JSOP_REGEXP, to avoid all this RegExp object cloning business.
1688
 
 *
1689
 
 * Why clone regexp objects?  ECMA specifies that when a regular expression
1690
 
 * literal is scanned, a RegExp object is created.  In the spec, compilation
1691
 
 * and execution happen indivisibly, but in this implementation and many of
1692
 
 * its embeddings, code is precompiled early and re-executed in multiple
1693
 
 * threads, or using multiple global objects, or both, for efficiency.
1694
 
 *
1695
 
 * In such cases, naively following ECMA leads to wrongful sharing of RegExp
1696
 
 * objects, which makes for collisions on the lastIndex property (especially
1697
 
 * for global regexps) and on any ad-hoc properties.  Also, __proto__ and
1698
 
 * __parent__ refer to the pre-compilation prototype and global objects, a
1699
 
 * pigeon-hole problem for instanceof tests.
1700
 
 */
1701
 
static JSBool
1702
 
IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale,
1703
 
                 JSCodeGenerator *cg)
1704
 
{
1705
 
    JSObject *varobj, *reobj;
1706
 
    JSClass *clasp;
1707
 
    JSFunction *fun;
1708
 
    JSRegExp *re;
1709
 
    uint16 *countPtr;
1710
 
    uintN cloneIndex;
1711
 
 
1712
 
    JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO)));
1713
 
 
1714
 
    varobj = cx->fp->varobj;
1715
 
    clasp = OBJ_GET_CLASS(cx, varobj);
1716
 
    if (clasp == &js_FunctionClass) {
1717
 
        fun = (JSFunction *) JS_GetPrivate(cx, varobj);
1718
 
        countPtr = &fun->u.i.nregexps;
1719
 
        cloneIndex = *countPtr;
1720
 
    } else {
1721
 
        JS_ASSERT(clasp != &js_CallClass);
1722
 
        countPtr = &cg->treeContext.numGlobalVars;
1723
 
        cloneIndex = ALE_INDEX(ale);
1724
 
    }
1725
 
 
1726
 
    if ((cloneIndex + 1) >> 16) {
1727
 
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1728
 
                             JSMSG_NEED_DIET, js_script_str);
1729
 
        return JS_FALSE;
1730
 
    }
1731
 
    if (cloneIndex >= *countPtr)
1732
 
        *countPtr = cloneIndex + 1;
1733
 
 
1734
 
    reobj = ATOM_TO_OBJECT(pn->pn_atom);
1735
 
    JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass);
1736
 
    re = (JSRegExp *) JS_GetPrivate(cx, reobj);
1737
 
    re->cloneIndex = cloneIndex;
1738
 
    return JS_TRUE;
1739
 
}
1740
 
 
1741
 
/*
1742
 
 * Emit a bytecode and its 2-byte constant (atom) index immediate operand.
1743
 
 * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit
1744
 
 * immediate operand indexes the atom in script->atomMap.
1745
 
 *
1746
 
 * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in
1747
 
 * the scope chain in which the literal name was found, followed by the name
1748
 
 * as a string.  This enables us to use the JOF_ELEM counterpart to op.
1749
 
 *
1750
 
 * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push
1751
 
 * the atom's value key.  For JOF_PROP ops, the object being operated on has
1752
 
 * already been pushed, and JSOP_LITERAL will push the id, leaving the stack
1753
 
 * in the proper state for a JOF_ELEM counterpart.
1754
 
 *
1755
 
 * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special
1756
 
 * dispatch on op, but getting op's atom index from the stack instead of from
1757
 
 * an unsigned 16-bit immediate operand.
1758
 
 */
1759
 
static JSBool
1760
 
EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg)
1761
 
{
1762
 
    uint32 mode;
1763
 
    JSOp prefixOp;
1764
 
    ptrdiff_t off;
1765
 
    jsbytecode *pc;
1766
 
 
1767
 
    if (atomIndex >= JS_BIT(16)) {
1768
 
        mode = (js_CodeSpec[op].format & JOF_MODEMASK);
1769
 
        if (op != JSOP_SETNAME) {
1770
 
            prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) ||
1771
 
#if JS_HAS_XML_SUPPORT
1772
 
                        op == JSOP_GETMETHOD ||
1773
 
                        op == JSOP_SETMETHOD ||
1774
 
#endif
1775
 
                        op == JSOP_SETCONST)
1776
 
                       ? JSOP_LITOPX
1777
 
                       : (mode == JOF_NAME)
1778
 
                       ? JSOP_FINDNAME
1779
 
                       : JSOP_LITERAL;
1780
 
            off = js_EmitN(cx, cg, prefixOp, 3);
1781
 
            if (off < 0)
1782
 
                return JS_FALSE;
1783
 
            pc = CG_CODE(cg, off);
1784
 
            SET_LITERAL_INDEX(pc, atomIndex);
1785
 
        }
1786
 
 
1787
 
        switch (op) {
1788
 
          case JSOP_DECNAME:    op = JSOP_DECELEM; break;
1789
 
          case JSOP_DECPROP:    op = JSOP_DECELEM; break;
1790
 
          case JSOP_DELNAME:    op = JSOP_DELELEM; break;
1791
 
          case JSOP_DELPROP:    op = JSOP_DELELEM; break;
1792
 
          case JSOP_FORNAME:    op = JSOP_FORELEM; break;
1793
 
          case JSOP_FORPROP:    op = JSOP_FORELEM; break;
1794
 
          case JSOP_GETPROP:    op = JSOP_GETELEM; break;
1795
 
          case JSOP_GETXPROP:   op = JSOP_GETXELEM; break;
1796
 
          case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break;
1797
 
          case JSOP_INCNAME:    op = JSOP_INCELEM; break;
1798
 
          case JSOP_INCPROP:    op = JSOP_INCELEM; break;
1799
 
          case JSOP_INITPROP:   op = JSOP_INITELEM; break;
1800
 
          case JSOP_NAME:       op = JSOP_GETELEM; break;
1801
 
          case JSOP_NAMEDEC:    op = JSOP_ELEMDEC; break;
1802
 
          case JSOP_NAMEINC:    op = JSOP_ELEMINC; break;
1803
 
          case JSOP_PROPDEC:    op = JSOP_ELEMDEC; break;
1804
 
          case JSOP_PROPINC:    op = JSOP_ELEMINC; break;
1805
 
          case JSOP_BINDNAME:   return JS_TRUE;
1806
 
          case JSOP_SETNAME:    op = JSOP_SETELEM; break;
1807
 
          case JSOP_SETPROP:    op = JSOP_SETELEM; break;
1808
 
#if JS_HAS_EXPORT_IMPORT
1809
 
          case JSOP_EXPORTNAME:
1810
 
            ReportStatementTooLarge(cx, cg);
1811
 
            return JS_FALSE;
1812
 
#endif
1813
 
          default:
1814
 
#if JS_HAS_XML_SUPPORT
1815
 
            JS_ASSERT(mode == 0 || op == JSOP_SETCONST ||
1816
 
                      op == JSOP_GETMETHOD || op == JSOP_SETMETHOD);
1817
 
#else
1818
 
            JS_ASSERT(mode == 0 || op == JSOP_SETCONST);
1819
 
#endif
1820
 
            break;
1821
 
        }
1822
 
 
1823
 
        return js_Emit1(cx, cg, op) >= 0;
1824
 
    }
1825
 
 
1826
 
    EMIT_UINT16_IMM_OP(op, atomIndex);
1827
 
    return JS_TRUE;
1828
 
}
1829
 
 
1830
 
/*
1831
 
 * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro
1832
 
 * caller's lexical environment, and embedding a false return on error.
1833
 
 * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?!
1834
 
 */
1835
 
#define EMIT_ATOM_INDEX_OP(op, atomIndex)                                     \
1836
 
    JS_BEGIN_MACRO                                                            \
1837
 
        if (!EmitAtomIndexOp(cx, op, atomIndex, cg))                          \
1838
 
            return JS_FALSE;                                                  \
1839
 
    JS_END_MACRO
1840
 
 
1841
 
static JSBool
1842
 
EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
1843
 
{
1844
 
    JSAtomListElement *ale;
1845
 
 
1846
 
    ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
1847
 
    if (!ale)
1848
 
        return JS_FALSE;
1849
 
    if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg))
1850
 
        return JS_FALSE;
1851
 
    return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg);
1852
 
}
1853
 
 
1854
 
/*
1855
 
 * This routine tries to optimize name gets and sets to stack slot loads and
1856
 
 * stores, given the variables object and scope chain in cx's top frame, the
1857
 
 * compile-time context in tc, and a TOK_NAME node pn.  It returns false on
1858
 
 * error, true on success.
1859
 
 *
1860
 
 * The caller can inspect pn->pn_slot for a non-negative slot number to tell
1861
 
 * whether optimization occurred, in which case BindNameToSlot also updated
1862
 
 * pn->pn_op.  If pn->pn_slot is still -1 on return, pn->pn_op nevertheless
1863
 
 * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS.  Whether
1864
 
 * or not pn->pn_op was modified, if this function finds an argument or local
1865
 
 * variable name, pn->pn_attrs will contain the property's attributes after a
1866
 
 * successful return.
1867
 
 *
1868
 
 * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget
1869
 
 * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases
1870
 
 * in js_EmitTree.
1871
 
 */
1872
 
static JSBool
1873
 
BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
1874
 
               JSBool letdecl)
1875
 
{
1876
 
    JSAtom *atom;
1877
 
    JSStmtInfo *stmt;
1878
 
    jsint slot;
1879
 
    JSOp op;
1880
 
    JSStackFrame *fp;
1881
 
    JSObject *obj, *pobj;
1882
 
    JSClass *clasp;
1883
 
    JSBool optimizeGlobals;
1884
 
    JSPropertyOp getter;
1885
 
    uintN attrs;
1886
 
    JSAtomListElement *ale;
1887
 
    JSProperty *prop;
1888
 
    JSScopeProperty *sprop;
1889
 
 
1890
 
    JS_ASSERT(pn->pn_type == TOK_NAME);
1891
 
    if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
1892
 
        return JS_TRUE;
1893
 
 
1894
 
    /* QNAME references can never be optimized to use arg/var storage. */
1895
 
    if (pn->pn_op == JSOP_QNAMEPART)
1896
 
        return JS_TRUE;
1897
 
 
1898
 
    /*
1899
 
     * We can't optimize if we are compiling a with statement and its body,
1900
 
     * or we're in a catch block whose exception variable has the same name
1901
 
     * as this node.  FIXME: we should be able to optimize catch vars to be
1902
 
     * block-locals.
1903
 
     */
1904
 
    atom = pn->pn_atom;
1905
 
    stmt = js_LexicalLookup(tc, atom, &slot, letdecl);
1906
 
    if (stmt) {
1907
 
        if (stmt->type == STMT_WITH)
1908
 
            return JS_TRUE;
1909
 
 
1910
 
        JS_ASSERT(stmt->flags & SIF_SCOPE);
1911
 
        JS_ASSERT(slot >= 0);
1912
 
        op = pn->pn_op;
1913
 
        switch (op) {
1914
 
          case JSOP_NAME:     op = JSOP_GETLOCAL; break;
1915
 
          case JSOP_SETNAME:  op = JSOP_SETLOCAL; break;
1916
 
          case JSOP_INCNAME:  op = JSOP_INCLOCAL; break;
1917
 
          case JSOP_NAMEINC:  op = JSOP_LOCALINC; break;
1918
 
          case JSOP_DECNAME:  op = JSOP_DECLOCAL; break;
1919
 
          case JSOP_NAMEDEC:  op = JSOP_LOCALDEC; break;
1920
 
          case JSOP_FORNAME:  op = JSOP_FORLOCAL; break;
1921
 
          case JSOP_DELNAME:  op = JSOP_FALSE; break;
1922
 
          default: JS_ASSERT(0);
1923
 
        }
1924
 
        if (op != pn->pn_op) {
1925
 
            pn->pn_op = op;
1926
 
            pn->pn_slot = slot;
1927
 
        }
1928
 
        return JS_TRUE;
1929
 
    }
1930
 
 
1931
 
    /*
1932
 
     * A Script object can be used to split an eval into a compile step done
1933
 
     * at construction time, and an execute step done separately, possibly in
1934
 
     * a different scope altogether.  We therefore cannot do any name-to-slot
1935
 
     * optimizations, but must lookup names at runtime.  Note that script_exec
1936
 
     * ensures that its caller's frame has a Call object, so arg and var name
1937
 
     * lookups will succeed.
1938
 
     */
1939
 
    fp = cx->fp;
1940
 
    if (fp->flags & JSFRAME_SCRIPT_OBJECT)
1941
 
        return JS_TRUE;
1942
 
 
1943
 
    /*
1944
 
     * We can't optimize if var and closure (a local function not in a larger
1945
 
     * expression and not at top-level within another's body) collide.
1946
 
     * XXX suboptimal: keep track of colliding names and deoptimize only those
1947
 
     */
1948
 
    if (tc->flags & TCF_FUN_CLOSURE_VS_VAR)
1949
 
        return JS_TRUE;
1950
 
 
1951
 
    /*
1952
 
     * We can't optimize if we're not compiling a function body, whether via
1953
 
     * eval, or directly when compiling a function statement or expression.
1954
 
     */
1955
 
    obj = fp->varobj;
1956
 
    clasp = OBJ_GET_CLASS(cx, obj);
1957
 
    if (clasp != &js_FunctionClass && clasp != &js_CallClass) {
1958
 
        /* Check for an eval or debugger frame. */
1959
 
        if (fp->flags & JSFRAME_SPECIAL)
1960
 
            return JS_TRUE;
1961
 
 
1962
 
        /*
1963
 
         * Optimize global variable accesses if there are at least 100 uses
1964
 
         * in unambiguous contexts, or failing that, if least half of all the
1965
 
         * uses of global vars/consts/functions are in loops.
1966
 
         */
1967
 
        optimizeGlobals = (tc->globalUses >= 100 ||
1968
 
                           (tc->loopyGlobalUses &&
1969
 
                            tc->loopyGlobalUses >= tc->globalUses / 2));
1970
 
        if (!optimizeGlobals)
1971
 
            return JS_TRUE;
1972
 
    } else {
1973
 
        optimizeGlobals = JS_FALSE;
1974
 
    }
1975
 
 
1976
 
    /*
1977
 
     * We can't optimize if we are in an eval called inside a with statement.
1978
 
     */
1979
 
    if (fp->scopeChain != obj)
1980
 
        return JS_TRUE;
1981
 
 
1982
 
    op = pn->pn_op;
1983
 
    getter = NULL;
1984
 
#ifdef __GNUC__
1985
 
    attrs = slot = 0;   /* quell GCC overwarning */
1986
 
#endif
1987
 
    if (optimizeGlobals) {
1988
 
        /*
1989
 
         * We are optimizing global variables, and there is no pre-existing
1990
 
         * global property named atom.  If atom was declared via const or var,
1991
 
         * optimize pn to access fp->vars using the appropriate JOF_QVAR op.
1992
 
         */
1993
 
        ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1994
 
        if (!ale) {
1995
 
            /* Use precedes declaration, or name is never declared. */
1996
 
            return JS_TRUE;
1997
 
        }
1998
 
 
1999
 
        attrs = (ALE_JSOP(ale) == JSOP_DEFCONST)
2000
 
                ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT
2001
 
                : JSPROP_ENUMERATE | JSPROP_PERMANENT;
2002
 
 
2003
 
        /* Index atom so we can map fast global number to name. */
2004
 
        JS_ASSERT(tc->flags & TCF_COMPILING);
2005
 
        ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList);
2006
 
        if (!ale)
2007
 
            return JS_FALSE;
2008
 
 
2009
 
        /* Defend against tc->numGlobalVars 16-bit overflow. */
2010
 
        slot = ALE_INDEX(ale);
2011
 
        if ((slot + 1) >> 16)
2012
 
            return JS_TRUE;
2013
 
 
2014
 
        if ((uint16)(slot + 1) > tc->numGlobalVars)
2015
 
            tc->numGlobalVars = (uint16)(slot + 1);
2016
 
    } else {
2017
 
        /*
2018
 
         * We may be able to optimize name to stack slot. Look for an argument
2019
 
         * or variable property in the function, or its call object, not found
2020
 
         * in any prototype object.  Rewrite pn_op and update pn accordingly.
2021
 
         * NB: We know that JSOP_DELNAME on an argument or variable evaluates
2022
 
         * to false, due to JSPROP_PERMANENT.
2023
 
         */
2024
 
        if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
2025
 
            return JS_FALSE;
2026
 
        sprop = (JSScopeProperty *) prop;
2027
 
        if (sprop) {
2028
 
            if (pobj == obj) {
2029
 
                getter = sprop->getter;
2030
 
                attrs = sprop->attrs;
2031
 
                slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1;
2032
 
            }
2033
 
            OBJ_DROP_PROPERTY(cx, pobj, prop);
2034
 
        }
2035
 
    }
2036
 
 
2037
 
    if (optimizeGlobals || getter) {
2038
 
        if (optimizeGlobals) {
2039
 
            switch (op) {
2040
 
              case JSOP_NAME:     op = JSOP_GETGVAR; break;
2041
 
              case JSOP_SETNAME:  op = JSOP_SETGVAR; break;
2042
 
              case JSOP_SETCONST: /* NB: no change */ break;
2043
 
              case JSOP_INCNAME:  op = JSOP_INCGVAR; break;
2044
 
              case JSOP_NAMEINC:  op = JSOP_GVARINC; break;
2045
 
              case JSOP_DECNAME:  op = JSOP_DECGVAR; break;
2046
 
              case JSOP_NAMEDEC:  op = JSOP_GVARDEC; break;
2047
 
              case JSOP_FORNAME:  /* NB: no change */ break;
2048
 
              case JSOP_DELNAME:  /* NB: no change */ break;
2049
 
              default: JS_ASSERT(0);
2050
 
            }
2051
 
        } else if (getter == js_GetLocalVariable ||
2052
 
                   getter == js_GetCallVariable) {
2053
 
            switch (op) {
2054
 
              case JSOP_NAME:     op = JSOP_GETVAR; break;
2055
 
              case JSOP_SETNAME:  op = JSOP_SETVAR; break;
2056
 
              case JSOP_SETCONST: op = JSOP_SETVAR; break;
2057
 
              case JSOP_INCNAME:  op = JSOP_INCVAR; break;
2058
 
              case JSOP_NAMEINC:  op = JSOP_VARINC; break;
2059
 
              case JSOP_DECNAME:  op = JSOP_DECVAR; break;
2060
 
              case JSOP_NAMEDEC:  op = JSOP_VARDEC; break;
2061
 
              case JSOP_FORNAME:  op = JSOP_FORVAR; break;
2062
 
              case JSOP_DELNAME:  op = JSOP_FALSE; break;
2063
 
              default: JS_ASSERT(0);
2064
 
            }
2065
 
        } else if (getter == js_GetArgument ||
2066
 
                   (getter == js_CallClass.getProperty &&
2067
 
                    fp->fun && (uintN) slot < fp->fun->nargs)) {
2068
 
            switch (op) {
2069
 
              case JSOP_NAME:     op = JSOP_GETARG; break;
2070
 
              case JSOP_SETNAME:  op = JSOP_SETARG; break;
2071
 
              case JSOP_INCNAME:  op = JSOP_INCARG; break;
2072
 
              case JSOP_NAMEINC:  op = JSOP_ARGINC; break;
2073
 
              case JSOP_DECNAME:  op = JSOP_DECARG; break;
2074
 
              case JSOP_NAMEDEC:  op = JSOP_ARGDEC; break;
2075
 
              case JSOP_FORNAME:  op = JSOP_FORARG; break;
2076
 
              case JSOP_DELNAME:  op = JSOP_FALSE; break;
2077
 
              default: JS_ASSERT(0);
2078
 
            }
2079
 
        }
2080
 
        if (op != pn->pn_op) {
2081
 
            pn->pn_op = op;
2082
 
            pn->pn_slot = slot;
2083
 
        }
2084
 
        pn->pn_attrs = attrs;
2085
 
    }
2086
 
 
2087
 
    if (pn->pn_slot < 0) {
2088
 
        /*
2089
 
         * We couldn't optimize pn, so it's not a global or local slot name.
2090
 
         * Now we must check for the predefined arguments variable.  It may be
2091
 
         * overridden by assignment, in which case the function is heavyweight
2092
 
         * and the interpreter will look up 'arguments' in the function's call
2093
 
         * object.
2094
 
         */
2095
 
        if (pn->pn_op == JSOP_NAME &&
2096
 
            atom == cx->runtime->atomState.argumentsAtom) {
2097
 
            pn->pn_op = JSOP_ARGUMENTS;
2098
 
            return JS_TRUE;
2099
 
        }
2100
 
 
2101
 
        tc->flags |= TCF_FUN_USES_NONLOCALS;
2102
 
    }
2103
 
    return JS_TRUE;
2104
 
}
2105
 
 
2106
 
/*
2107
 
 * If pn contains a useful expression, return true with *answer set to true.
2108
 
 * If pn contains a useless expression, return true with *answer set to false.
2109
 
 * Return false on error.
2110
 
 *
2111
 
 * The caller should initialize *answer to false and invoke this function on
2112
 
 * an expression statement or similar subtree to decide whether the tree could
2113
 
 * produce code that has any side effects.  For an expression statement, we
2114
 
 * define useless code as code with no side effects, because the main effect,
2115
 
 * the value left on the stack after the code executes, will be discarded by a
2116
 
 * pop bytecode.
2117
 
 */
2118
 
static JSBool
2119
 
CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
2120
 
                 JSBool *answer)
2121
 
{
2122
 
    JSBool ok;
2123
 
    JSFunction *fun;
2124
 
    JSParseNode *pn2;
2125
 
 
2126
 
    ok = JS_TRUE;
2127
 
    if (!pn || *answer)
2128
 
        return ok;
2129
 
 
2130
 
    switch (pn->pn_arity) {
2131
 
      case PN_FUNC:
2132
 
        /*
2133
 
         * A named function is presumed useful: we can't yet know that it is
2134
 
         * not called.  The side effects are the creation of a scope object
2135
 
         * to parent this function object, and the binding of the function's
2136
 
         * name in that scope object.  See comments at case JSOP_NAMEDFUNOBJ:
2137
 
         * in jsinterp.c.
2138
 
         */
2139
 
        fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
2140
 
        if (fun->atom)
2141
 
            *answer = JS_TRUE;
2142
 
        break;
2143
 
 
2144
 
      case PN_LIST:
2145
 
        if (pn->pn_type == TOK_NEW ||
2146
 
            pn->pn_type == TOK_LP ||
2147
 
            pn->pn_type == TOK_LB ||
2148
 
            pn->pn_type == TOK_RB ||
2149
 
            pn->pn_type == TOK_RC) {
2150
 
            /*
2151
 
             * All invocation operations (construct: TOK_NEW, call: TOK_LP)
2152
 
             * are presumed to be useful, because they may have side effects
2153
 
             * even if their main effect (their return value) is discarded.
2154
 
             *
2155
 
             * TOK_LB binary trees of 3 or more nodes are flattened into lists
2156
 
             * to avoid too much recursion.  All such lists must be presumed
2157
 
             * to be useful because each index operation could invoke a getter
2158
 
             * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case,
2159
 
             * does not apply here: arguments[i][j] might invoke a getter).
2160
 
             *
2161
 
             * Array and object initializers (TOK_RB and TOK_RC lists) must be
2162
 
             * considered useful, because they are sugar for constructor calls
2163
 
             * (to Array and Object, respectively).
2164
 
             */
2165
 
            *answer = JS_TRUE;
2166
 
        } else {
2167
 
            for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
2168
 
                ok &= CheckSideEffects(cx, tc, pn2, answer);
2169
 
        }
2170
 
        break;
2171
 
 
2172
 
      case PN_TERNARY:
2173
 
        ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) &&
2174
 
             CheckSideEffects(cx, tc, pn->pn_kid2, answer) &&
2175
 
             CheckSideEffects(cx, tc, pn->pn_kid3, answer);
2176
 
        break;
2177
 
 
2178
 
      case PN_BINARY:
2179
 
        if (pn->pn_type == TOK_ASSIGN) {
2180
 
            /*
2181
 
             * Assignment is presumed to be useful, even if the next operation
2182
 
             * is another assignment overwriting this one's ostensible effect,
2183
 
             * because the left operand may be a property with a setter that
2184
 
             * has side effects.
2185
 
             *
2186
 
             * The only exception is assignment of a useless value to a const
2187
 
             * declared in the function currently being compiled.
2188
 
             */
2189
 
            pn2 = pn->pn_left;
2190
 
            if (pn2->pn_type != TOK_NAME) {
2191
 
                *answer = JS_TRUE;
2192
 
            } else {
2193
 
                if (!BindNameToSlot(cx, tc, pn2, JS_FALSE))
2194
 
                    return JS_FALSE;
2195
 
                if (!CheckSideEffects(cx, tc, pn->pn_right, answer))
2196
 
                    return JS_FALSE;
2197
 
                if (!*answer &&
2198
 
                    (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY))) {
2199
 
                    *answer = JS_TRUE;
2200
 
                }
2201
 
            }
2202
 
        } else {
2203
 
            if (pn->pn_type == TOK_LB) {
2204
 
                pn2 = pn->pn_left;
2205
 
                if (pn2->pn_type == TOK_NAME &&
2206
 
                    !BindNameToSlot(cx, tc, pn2, JS_FALSE)) {
2207
 
                    return JS_FALSE;
2208
 
                }
2209
 
                if (pn2->pn_op != JSOP_ARGUMENTS) {
2210
 
                    /*
2211
 
                     * Any indexed property reference could call a getter with
2212
 
                     * side effects, except for arguments[i] where arguments is
2213
 
                     * unambiguous.
2214
 
                     */
2215
 
                    *answer = JS_TRUE;
2216
 
                }
2217
 
            }
2218
 
            ok = CheckSideEffects(cx, tc, pn->pn_left, answer) &&
2219
 
                 CheckSideEffects(cx, tc, pn->pn_right, answer);
2220
 
        }
2221
 
        break;
2222
 
 
2223
 
      case PN_UNARY:
2224
 
        if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC ||
2225
 
            pn->pn_type == TOK_THROW ||
2226
 
#if JS_HAS_GENERATORS
2227
 
            pn->pn_type == TOK_YIELD ||
2228
 
#endif
2229
 
            pn->pn_type == TOK_DEFSHARP) {
2230
 
            /* All these operations have effects that we must commit. */
2231
 
            *answer = JS_TRUE;
2232
 
        } else if (pn->pn_type == TOK_DELETE) {
2233
 
            pn2 = pn->pn_kid;
2234
 
            switch (pn2->pn_type) {
2235
 
              case TOK_NAME:
2236
 
              case TOK_DOT:
2237
 
#if JS_HAS_XML_SUPPORT
2238
 
              case TOK_DBLDOT:
2239
 
#endif
2240
 
#if JS_HAS_LVALUE_RETURN
2241
 
              case TOK_LP:
2242
 
#endif
2243
 
              case TOK_LB:
2244
 
                /* All these delete addressing modes have effects too. */
2245
 
                *answer = JS_TRUE;
2246
 
                break;
2247
 
              default:
2248
 
                ok = CheckSideEffects(cx, tc, pn2, answer);
2249
 
                break;
2250
 
            }
2251
 
        } else {
2252
 
            ok = CheckSideEffects(cx, tc, pn->pn_kid, answer);
2253
 
        }
2254
 
        break;
2255
 
 
2256
 
      case PN_NAME:
2257
 
        /*
2258
 
         * Take care to avoid trying to bind a label name (labels, both for
2259
 
         * statements and property values in object initialisers, have pn_op
2260
 
         * defaulted to JSOP_NOP).
2261
 
         */
2262
 
        if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) {
2263
 
            if (!BindNameToSlot(cx, tc, pn, JS_FALSE))
2264
 
                return JS_FALSE;
2265
 
            if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) {
2266
 
                /*
2267
 
                 * Not an argument or local variable use, so this expression
2268
 
                 * could invoke a getter that has side effects.
2269
 
                 */
2270
 
                *answer = JS_TRUE;
2271
 
            }
2272
 
        }
2273
 
        pn2 = pn->pn_expr;
2274
 
        if (pn->pn_type == TOK_DOT) {
2275
 
            if (pn2->pn_type == TOK_NAME &&
2276
 
                !BindNameToSlot(cx, tc, pn2, JS_FALSE)) {
2277
 
                return JS_FALSE;
2278
 
            }
2279
 
            if (!(pn2->pn_op == JSOP_ARGUMENTS &&
2280
 
                  pn->pn_atom == cx->runtime->atomState.lengthAtom)) {
2281
 
                /*
2282
 
                 * Any dotted property reference could call a getter, except
2283
 
                 * for arguments.length where arguments is unambiguous.
2284
 
                 */
2285
 
                *answer = JS_TRUE;
2286
 
            }
2287
 
        }
2288
 
        ok = CheckSideEffects(cx, tc, pn2, answer);
2289
 
        break;
2290
 
 
2291
 
      case PN_NULLARY:
2292
 
        if (pn->pn_type == TOK_DEBUGGER)
2293
 
            *answer = JS_TRUE;
2294
 
        break;
2295
 
    }
2296
 
    return ok;
2297
 
}
2298
 
 
2299
 
/*
2300
 
 * Secret handshake with js_EmitTree's TOK_LP/TOK_NEW case logic, to flag all
2301
 
 * uses of JSOP_GETMETHOD that implicitly qualify the method property's name
2302
 
 * with a function:: prefix.  All other JSOP_GETMETHOD and JSOP_SETMETHOD uses
2303
 
 * must be explicit, so we need a distinct source note (SRC_METHODBASE rather
2304
 
 * than SRC_PCBASE) for round-tripping through the beloved decompiler.
2305
 
 */
2306
 
#define JSPROP_IMPLICIT_FUNCTION_NAMESPACE      0x100
2307
 
 
2308
 
static jssrcnote
2309
 
SrcNoteForPropOp(JSParseNode *pn, JSOp op)
2310
 
{
2311
 
    return ((op == JSOP_GETMETHOD &&
2312
 
             !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) ||
2313
 
            op == JSOP_SETMETHOD)
2314
 
           ? SRC_METHODBASE
2315
 
           : SRC_PCBASE;
2316
 
}
2317
 
 
2318
 
static JSBool
2319
 
EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
2320
 
{
2321
 
    JSParseNode *pn2, *pndot, *pnup, *pndown;
2322
 
    ptrdiff_t top;
2323
 
 
2324
 
    pn2 = pn->pn_expr;
2325
 
    if (op == JSOP_GETPROP &&
2326
 
        pn->pn_type == TOK_DOT &&
2327
 
        pn2->pn_type == TOK_NAME) {
2328
 
        /* Try to optimize arguments.length into JSOP_ARGCNT. */
2329
 
        if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
2330
 
            return JS_FALSE;
2331
 
        if (pn2->pn_op == JSOP_ARGUMENTS &&
2332
 
            pn->pn_atom == cx->runtime->atomState.lengthAtom) {
2333
 
            return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0;
2334
 
        }
2335
 
    }
2336
 
 
2337
 
    /*
2338
 
     * If the object operand is also a dotted property reference, reverse the
2339
 
     * list linked via pn_expr temporarily so we can iterate over it from the
2340
 
     * bottom up (reversing again as we go), to avoid excessive recursion.
2341
 
     */
2342
 
    if (pn2->pn_type == TOK_DOT) {
2343
 
        pndot = pn2;
2344
 
        pnup = NULL;
2345
 
        top = CG_OFFSET(cg);
2346
 
        for (;;) {
2347
 
            /* Reverse pndot->pn_expr to point up, not down. */
2348
 
            pndot->pn_offset = top;
2349
 
            pndown = pndot->pn_expr;
2350
 
            pndot->pn_expr = pnup;
2351
 
            if (pndown->pn_type != TOK_DOT)
2352
 
                break;
2353
 
            pnup = pndot;
2354
 
            pndot = pndown;
2355
 
        }
2356
 
 
2357
 
        /* pndown is a primary expression, not a dotted property reference. */
2358
 
        if (!js_EmitTree(cx, cg, pndown))
2359
 
            return JS_FALSE;
2360
 
 
2361
 
        do {
2362
 
            /* Walk back up the list, emitting annotated name ops. */
2363
 
            if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pndot, pndot->pn_op),
2364
 
                               CG_OFFSET(cg) - pndown->pn_offset) < 0) {
2365
 
                return JS_FALSE;
2366
 
            }
2367
 
            if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg))
2368
 
                return JS_FALSE;
2369
 
 
2370
 
            /* Reverse the pn_expr link again. */
2371
 
            pnup = pndot->pn_expr;
2372
 
            pndot->pn_expr = pndown;
2373
 
            pndown = pndot;
2374
 
        } while ((pndot = pnup) != NULL);
2375
 
    } else {
2376
 
        if (!js_EmitTree(cx, cg, pn2))
2377
 
            return JS_FALSE;
2378
 
    }
2379
 
 
2380
 
    if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op),
2381
 
                       CG_OFFSET(cg) - pn2->pn_offset) < 0) {
2382
 
        return JS_FALSE;
2383
 
    }
2384
 
    if (!pn->pn_atom) {
2385
 
        JS_ASSERT(op == JSOP_IMPORTALL);
2386
 
        if (js_Emit1(cx, cg, op) < 0)
2387
 
            return JS_FALSE;
2388
 
    } else {
2389
 
        if (!EmitAtomOp(cx, pn, op, cg))
2390
 
            return JS_FALSE;
2391
 
    }
2392
 
    return JS_TRUE;
2393
 
}
2394
 
 
2395
 
static JSBool
2396
 
EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
2397
 
{
2398
 
    ptrdiff_t top;
2399
 
    JSParseNode *left, *right, *next, ltmp, rtmp;
2400
 
    jsint slot;
2401
 
 
2402
 
    top = CG_OFFSET(cg);
2403
 
    if (pn->pn_arity == PN_LIST) {
2404
 
        /* Left-associative operator chain to avoid too much recursion. */
2405
 
        JS_ASSERT(pn->pn_op == JSOP_GETELEM || pn->pn_op == JSOP_IMPORTELEM);
2406
 
        JS_ASSERT(pn->pn_count >= 3);
2407
 
        left = pn->pn_head;
2408
 
        right = PN_LAST(pn);
2409
 
        next = left->pn_next;
2410
 
        JS_ASSERT(next != right);
2411
 
 
2412
 
        /*
2413
 
         * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by
2414
 
         * one or more index expression and JSOP_GETELEM op pairs.
2415
 
         */
2416
 
        if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
2417
 
            if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE))
2418
 
                return JS_FALSE;
2419
 
            if (left->pn_op == JSOP_ARGUMENTS &&
2420
 
                JSDOUBLE_IS_INT(next->pn_dval, slot) &&
2421
 
                (jsuint)slot < JS_BIT(16)) {
2422
 
                left->pn_offset = next->pn_offset = top;
2423
 
                EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
2424
 
                left = next;
2425
 
                next = left->pn_next;
2426
 
            }
2427
 
        }
2428
 
 
2429
 
        /*
2430
 
         * Check whether we generated JSOP_ARGSUB, just above, and have only
2431
 
         * one more index expression to emit.  Given arguments[0][j], we must
2432
 
         * skip the while loop altogether, falling through to emit code for j
2433
 
         * (in the subtree referenced by right), followed by the annotated op,
2434
 
         * at the bottom of this function.
2435
 
         */
2436
 
        JS_ASSERT(next != right || pn->pn_count == 3);
2437
 
        if (left == pn->pn_head) {
2438
 
            if (!js_EmitTree(cx, cg, left))
2439
 
                return JS_FALSE;
2440
 
        }
2441
 
        while (next != right) {
2442
 
            if (!js_EmitTree(cx, cg, next))
2443
 
                return JS_FALSE;
2444
 
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
2445
 
                return JS_FALSE;
2446
 
            if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
2447
 
                return JS_FALSE;
2448
 
            next = next->pn_next;
2449
 
        }
2450
 
    } else {
2451
 
        if (pn->pn_arity == PN_NAME) {
2452
 
            /*
2453
 
             * Set left and right so pn appears to be a TOK_LB node, instead
2454
 
             * of a TOK_DOT node.  See the TOK_FOR/IN case in js_EmitTree, and
2455
 
             * EmitDestructuringOps nearer below.  In the destructuring case,
2456
 
             * the base expression (pn_expr) of the name may be null, which
2457
 
             * means we have to emit a JSOP_BINDNAME.
2458
 
             */
2459
 
            left = pn->pn_expr;
2460
 
            if (!left) {
2461
 
                left = &ltmp;
2462
 
                left->pn_type = TOK_OBJECT;
2463
 
                left->pn_op = JSOP_BINDNAME;
2464
 
                left->pn_arity = PN_NULLARY;
2465
 
                left->pn_pos = pn->pn_pos;
2466
 
                left->pn_atom = pn->pn_atom;
2467
 
            }
2468
 
            right = &rtmp;
2469
 
            right->pn_type = TOK_STRING;
2470
 
            JS_ASSERT(ATOM_IS_STRING(pn->pn_atom));
2471
 
            right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom))
2472
 
                           ? JSOP_QNAMEPART
2473
 
                           : JSOP_STRING;
2474
 
            right->pn_arity = PN_NULLARY;
2475
 
            right->pn_pos = pn->pn_pos;
2476
 
            right->pn_atom = pn->pn_atom;
2477
 
        } else {
2478
 
            JS_ASSERT(pn->pn_arity == PN_BINARY);
2479
 
            left = pn->pn_left;
2480
 
            right = pn->pn_right;
2481
 
        }
2482
 
 
2483
 
        /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */
2484
 
        if (op == JSOP_GETELEM &&
2485
 
            left->pn_type == TOK_NAME &&
2486
 
            right->pn_type == TOK_NUMBER) {
2487
 
            if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE))
2488
 
                return JS_FALSE;
2489
 
            if (left->pn_op == JSOP_ARGUMENTS &&
2490
 
                JSDOUBLE_IS_INT(right->pn_dval, slot) &&
2491
 
                (jsuint)slot < JS_BIT(16)) {
2492
 
                left->pn_offset = right->pn_offset = top;
2493
 
                EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
2494
 
                return JS_TRUE;
2495
 
            }
2496
 
        }
2497
 
 
2498
 
        if (!js_EmitTree(cx, cg, left))
2499
 
            return JS_FALSE;
2500
 
    }
2501
 
 
2502
 
    /* The right side of the descendant operator is implicitly quoted. */
2503
 
    JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING ||
2504
 
              right->pn_op == JSOP_QNAMEPART);
2505
 
    if (!js_EmitTree(cx, cg, right))
2506
 
        return JS_FALSE;
2507
 
    if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
2508
 
        return JS_FALSE;
2509
 
    return js_Emit1(cx, cg, op) >= 0;
2510
 
}
2511
 
 
2512
 
static JSBool
2513
 
EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
2514
 
{
2515
 
    jsint ival;
2516
 
    jsatomid atomIndex;
2517
 
    ptrdiff_t off;
2518
 
    jsbytecode *pc;
2519
 
    JSAtom *atom;
2520
 
    JSAtomListElement *ale;
2521
 
 
2522
 
    if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
2523
 
        if (ival == 0)
2524
 
            return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
2525
 
        if (ival == 1)
2526
 
            return js_Emit1(cx, cg, JSOP_ONE) >= 0;
2527
 
 
2528
 
        atomIndex = (jsatomid)ival;
2529
 
        if (atomIndex < JS_BIT(16)) {
2530
 
            EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex);
2531
 
            return JS_TRUE;
2532
 
        }
2533
 
 
2534
 
        if (atomIndex < JS_BIT(24)) {
2535
 
            off = js_EmitN(cx, cg, JSOP_UINT24, 3);
2536
 
            if (off < 0)
2537
 
                return JS_FALSE;
2538
 
            pc = CG_CODE(cg, off);
2539
 
            SET_LITERAL_INDEX(pc, atomIndex);
2540
 
            return JS_TRUE;
2541
 
        }
2542
 
 
2543
 
        atom = js_AtomizeInt(cx, ival, 0);
2544
 
    } else {
2545
 
        atom = js_AtomizeDouble(cx, dval, 0);
2546
 
    }
2547
 
    if (!atom)
2548
 
        return JS_FALSE;
2549
 
 
2550
 
    ale = js_IndexAtom(cx, atom, &cg->atomList);
2551
 
    if (!ale)
2552
 
        return JS_FALSE;
2553
 
    return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg);
2554
 
}
2555
 
 
2556
 
static JSBool
2557
 
EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
2558
 
           JSStmtInfo *stmtInfo)
2559
 
{
2560
 
    JSOp switchOp;
2561
 
    JSBool ok, hasDefault, constPropagated;
2562
 
    ptrdiff_t top, off, defaultOffset;
2563
 
    JSParseNode *pn2, *pn3, *pn4;
2564
 
    uint32 caseCount, tableLength;
2565
 
    JSParseNode **table;
2566
 
    jsdouble d;
2567
 
    jsint i, low, high;
2568
 
    jsval v;
2569
 
    JSAtom *atom;
2570
 
    JSAtomListElement *ale;
2571
 
    intN noteIndex;
2572
 
    size_t switchSize, tableSize;
2573
 
    jsbytecode *pc, *savepc;
2574
 
#if JS_HAS_BLOCK_SCOPE
2575
 
    JSObject *obj;
2576
 
    jsint count;
2577
 
#endif
2578
 
 
2579
 
    /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
2580
 
    switchOp = JSOP_TABLESWITCH;
2581
 
    ok = JS_TRUE;
2582
 
    hasDefault = constPropagated = JS_FALSE;
2583
 
    defaultOffset = -1;
2584
 
 
2585
 
    /*
2586
 
     * If the switch contains let variables scoped by its body, model the
2587
 
     * resulting block on the stack first, before emitting the discriminant's
2588
 
     * bytecode (in case the discriminant contains a stack-model dependency
2589
 
     * such as a let expression).
2590
 
     */
2591
 
    pn2 = pn->pn_right;
2592
 
#if JS_HAS_BLOCK_SCOPE
2593
 
    if (pn2->pn_type == TOK_LEXICALSCOPE) {
2594
 
        atom = pn2->pn_atom;
2595
 
        obj = ATOM_TO_OBJECT(atom);
2596
 
        OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
2597
 
 
2598
 
        /*
2599
 
         * Push the body's block scope before discriminant code-gen for proper
2600
 
         * static block scope linkage in case the discriminant contains a let
2601
 
         * expression.  The block's locals must lie under the discriminant on
2602
 
         * the stack so that case-dispatch bytecodes can find the discriminant
2603
 
         * on top of stack.
2604
 
         */
2605
 
        js_PushBlockScope(&cg->treeContext, stmtInfo, atom, -1);
2606
 
        stmtInfo->type = STMT_SWITCH;
2607
 
 
2608
 
        count = OBJ_BLOCK_COUNT(cx, obj);
2609
 
        cg->stackDepth += count;
2610
 
        if ((uintN)cg->stackDepth > cg->maxStackDepth)
2611
 
            cg->maxStackDepth = cg->stackDepth;
2612
 
 
2613
 
        /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */
2614
 
        ale = js_IndexAtom(cx, atom, &cg->atomList);
2615
 
        if (!ale)
2616
 
            return JS_FALSE;
2617
 
        EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));
2618
 
 
2619
 
        /*
2620
 
         * Pop the switch's statement info around discriminant code-gen.  Note
2621
 
         * how this leaves cg->treeContext.blockChain referencing the switch's
2622
 
         * block scope object, which is necessary for correct block parenting
2623
 
         * in the case where the discriminant contains a let expression.
2624
 
         */
2625
 
        cg->treeContext.topStmt = stmtInfo->down;
2626
 
        cg->treeContext.topScopeStmt = stmtInfo->downScope;
2627
 
    }
2628
 
#ifdef __GNUC__
2629
 
    else {
2630
 
        atom = NULL;
2631
 
        count = -1;
2632
 
    }
2633
 
#endif
2634
 
#endif
2635
 
 
2636
 
    /*
2637
 
     * Emit code for the discriminant first (or nearly first, in the case of a
2638
 
     * switch whose body is a block scope).
2639
 
     */
2640
 
    if (!js_EmitTree(cx, cg, pn->pn_left))
2641
 
        return JS_FALSE;
2642
 
 
2643
 
    /* Switch bytecodes run from here till end of final case. */
2644
 
    top = CG_OFFSET(cg);
2645
 
#if !JS_HAS_BLOCK_SCOPE
2646
 
    js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
2647
 
#else
2648
 
    if (pn2->pn_type == TOK_LC) {
2649
 
        js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
2650
 
    } else {
2651
 
        /* Re-push the switch's statement info record. */
2652
 
        cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo;
2653
 
 
2654
 
        /* Set the statement info record's idea of top. */
2655
 
        stmtInfo->update = top;
2656
 
 
2657
 
        /* Advance pn2 to refer to the switch case list. */
2658
 
        pn2 = pn2->pn_expr;
2659
 
    }
2660
 
#endif
2661
 
 
2662
 
    caseCount = pn2->pn_count;
2663
 
    tableLength = 0;
2664
 
    table = NULL;
2665
 
 
2666
 
    if (caseCount == 0 ||
2667
 
        (caseCount == 1 &&
2668
 
         (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) {
2669
 
        caseCount = 0;
2670
 
        low = 0;
2671
 
        high = -1;
2672
 
    } else {
2673
 
#define INTMAP_LENGTH   256
2674
 
        jsbitmap intmap_space[INTMAP_LENGTH];
2675
 
        jsbitmap *intmap = NULL;
2676
 
        int32 intmap_bitlen = 0;
2677
 
 
2678
 
        low  = JSVAL_INT_MAX;
2679
 
        high = JSVAL_INT_MIN;
2680
 
 
2681
 
        for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2682
 
            if (pn3->pn_type == TOK_DEFAULT) {
2683
 
                hasDefault = JS_TRUE;
2684
 
                caseCount--;    /* one of the "cases" was the default */
2685
 
                continue;
2686
 
            }
2687
 
 
2688
 
            JS_ASSERT(pn3->pn_type == TOK_CASE);
2689
 
            if (switchOp == JSOP_CONDSWITCH)
2690
 
                continue;
2691
 
 
2692
 
            pn4 = pn3->pn_left;
2693
 
            switch (pn4->pn_type) {
2694
 
              case TOK_NUMBER:
2695
 
                d = pn4->pn_dval;
2696
 
                if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
2697
 
                    pn3->pn_val = INT_TO_JSVAL(i);
2698
 
                } else {
2699
 
                    atom = js_AtomizeDouble(cx, d, 0);
2700
 
                    if (!atom) {
2701
 
                        ok = JS_FALSE;
2702
 
                        goto release;
2703
 
                    }
2704
 
                    pn3->pn_val = ATOM_KEY(atom);
2705
 
                }
2706
 
                break;
2707
 
              case TOK_STRING:
2708
 
                pn3->pn_val = ATOM_KEY(pn4->pn_atom);
2709
 
                break;
2710
 
              case TOK_NAME:
2711
 
                if (!pn4->pn_expr) {
2712
 
                    ok = js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v);
2713
 
                    if (!ok)
2714
 
                        goto release;
2715
 
                    if (!JSVAL_IS_VOID(v)) {
2716
 
                        pn3->pn_val = v;
2717
 
                        constPropagated = JS_TRUE;
2718
 
                        break;
2719
 
                    }
2720
 
                }
2721
 
                /* FALL THROUGH */
2722
 
              case TOK_PRIMARY:
2723
 
                if (pn4->pn_op == JSOP_TRUE) {
2724
 
                    pn3->pn_val = JSVAL_TRUE;
2725
 
                    break;
2726
 
                }
2727
 
                if (pn4->pn_op == JSOP_FALSE) {
2728
 
                    pn3->pn_val = JSVAL_FALSE;
2729
 
                    break;
2730
 
                }
2731
 
                /* FALL THROUGH */
2732
 
              default:
2733
 
                switchOp = JSOP_CONDSWITCH;
2734
 
                continue;
2735
 
            }
2736
 
 
2737
 
            JS_ASSERT(JSVAL_IS_NUMBER(pn3->pn_val) ||
2738
 
                      JSVAL_IS_STRING(pn3->pn_val) ||
2739
 
                      JSVAL_IS_BOOLEAN(pn3->pn_val));
2740
 
 
2741
 
            if (switchOp != JSOP_TABLESWITCH)
2742
 
                continue;
2743
 
            if (!JSVAL_IS_INT(pn3->pn_val)) {
2744
 
                switchOp = JSOP_LOOKUPSWITCH;
2745
 
                continue;
2746
 
            }
2747
 
            i = JSVAL_TO_INT(pn3->pn_val);
2748
 
            if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) {
2749
 
                switchOp = JSOP_LOOKUPSWITCH;
2750
 
                continue;
2751
 
            }
2752
 
            if (i < low)
2753
 
                low = i;
2754
 
            if (high < i)
2755
 
                high = i;
2756
 
 
2757
 
            /*
2758
 
             * Check for duplicates, which require a JSOP_LOOKUPSWITCH.
2759
 
             * We bias i by 65536 if it's negative, and hope that's a rare
2760
 
             * case (because it requires a malloc'd bitmap).
2761
 
             */
2762
 
            if (i < 0)
2763
 
                i += JS_BIT(16);
2764
 
            if (i >= intmap_bitlen) {
2765
 
                if (!intmap &&
2766
 
                    i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) {
2767
 
                    intmap = intmap_space;
2768
 
                    intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2;
2769
 
                } else {
2770
 
                    /* Just grab 8K for the worst-case bitmap. */
2771
 
                    intmap_bitlen = JS_BIT(16);
2772
 
                    intmap = (jsbitmap *)
2773
 
                        JS_malloc(cx,
2774
 
                                  (JS_BIT(16) >> JS_BITS_PER_WORD_LOG2)
2775
 
                                  * sizeof(jsbitmap));
2776
 
                    if (!intmap) {
2777
 
                        JS_ReportOutOfMemory(cx);
2778
 
                        return JS_FALSE;
2779
 
                    }
2780
 
                }
2781
 
                memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2);
2782
 
            }
2783
 
            if (JS_TEST_BIT(intmap, i)) {
2784
 
                switchOp = JSOP_LOOKUPSWITCH;
2785
 
                continue;
2786
 
            }
2787
 
            JS_SET_BIT(intmap, i);
2788
 
        }
2789
 
 
2790
 
      release:
2791
 
        if (intmap && intmap != intmap_space)
2792
 
            JS_free(cx, intmap);
2793
 
        if (!ok)
2794
 
            return JS_FALSE;
2795
 
 
2796
 
        /*
2797
 
         * Compute table length and select lookup instead if overlarge or
2798
 
         * more than half-sparse.
2799
 
         */
2800
 
        if (switchOp == JSOP_TABLESWITCH) {
2801
 
            tableLength = (uint32)(high - low + 1);
2802
 
            if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
2803
 
                switchOp = JSOP_LOOKUPSWITCH;
2804
 
        } else if (switchOp == JSOP_LOOKUPSWITCH) {
2805
 
            /*
2806
 
             * Lookup switch supports only atom indexes below 64K limit.
2807
 
             * Conservatively estimate the maximum possible index during
2808
 
             * switch generation and use conditional switch if it exceeds
2809
 
             * the limit.
2810
 
             */
2811
 
            if (caseCount + cg->atomList.count > JS_BIT(16))
2812
 
                switchOp = JSOP_CONDSWITCH;
2813
 
        }
2814
 
    }
2815
 
 
2816
 
    /*
2817
 
     * Emit a note with two offsets: first tells total switch code length,
2818
 
     * second tells offset to first JSOP_CASE if condswitch.
2819
 
     */
2820
 
    noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0);
2821
 
    if (noteIndex < 0)
2822
 
        return JS_FALSE;
2823
 
 
2824
 
    if (switchOp == JSOP_CONDSWITCH) {
2825
 
        /*
2826
 
         * 0 bytes of immediate for unoptimized ECMAv2 switch.
2827
 
         */
2828
 
        switchSize = 0;
2829
 
    } else if (switchOp == JSOP_TABLESWITCH) {
2830
 
        /*
2831
 
         * 3 offsets (len, low, high) before the table, 1 per entry.
2832
 
         */
2833
 
        switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
2834
 
    } else {
2835
 
        /*
2836
 
         * JSOP_LOOKUPSWITCH:
2837
 
         * 1 offset (len) and 1 atom index (npairs) before the table,
2838
 
         * 1 atom index and 1 jump offset per entry.
2839
 
         */
2840
 
        switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN +
2841
 
                              (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount);
2842
 
    }
2843
 
 
2844
 
    /*
2845
 
     * Emit switchOp followed by switchSize bytes of jump or lookup table.
2846
 
     *
2847
 
     * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial
2848
 
     * to emit the immediate operand(s) by which bytecode readers such as
2849
 
     * BuildSpanDepTable discover the length of the switch opcode *before*
2850
 
     * calling js_SetJumpOffset (which may call BuildSpanDepTable).  It's
2851
 
     * also important to zero all unknown jump offset immediate operands,
2852
 
     * so they can be converted to span dependencies with null targets to
2853
 
     * be computed later (js_EmitN zeros switchSize bytes after switchOp).
2854
 
     */
2855
 
    if (js_EmitN(cx, cg, switchOp, switchSize) < 0)
2856
 
        return JS_FALSE;
2857
 
 
2858
 
    off = -1;
2859
 
    if (switchOp == JSOP_CONDSWITCH) {
2860
 
        intN caseNoteIndex = -1;
2861
 
        JSBool beforeCases = JS_TRUE;
2862
 
 
2863
 
        /* Emit code for evaluating cases and jumping to case statements. */
2864
 
        for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2865
 
            pn4 = pn3->pn_left;
2866
 
            if (pn4 && !js_EmitTree(cx, cg, pn4))
2867
 
                return JS_FALSE;
2868
 
            if (caseNoteIndex >= 0) {
2869
 
                /* off is the previous JSOP_CASE's bytecode offset. */
2870
 
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0,
2871
 
                                         CG_OFFSET(cg) - off)) {
2872
 
                    return JS_FALSE;
2873
 
                }
2874
 
            }
2875
 
            if (!pn4) {
2876
 
                JS_ASSERT(pn3->pn_type == TOK_DEFAULT);
2877
 
                continue;
2878
 
            }
2879
 
            caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
2880
 
            if (caseNoteIndex < 0)
2881
 
                return JS_FALSE;
2882
 
            off = EmitJump(cx, cg, JSOP_CASE, 0);
2883
 
            if (off < 0)
2884
 
                return JS_FALSE;
2885
 
            pn3->pn_offset = off;
2886
 
            if (beforeCases) {
2887
 
                uintN noteCount, noteCountDelta;
2888
 
 
2889
 
                /* Switch note's second offset is to first JSOP_CASE. */
2890
 
                noteCount = CG_NOTE_COUNT(cg);
2891
 
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
2892
 
                                         off - top)) {
2893
 
                    return JS_FALSE;
2894
 
                }
2895
 
                noteCountDelta = CG_NOTE_COUNT(cg) - noteCount;
2896
 
                if (noteCountDelta != 0)
2897
 
                    caseNoteIndex += noteCountDelta;
2898
 
                beforeCases = JS_FALSE;
2899
 
            }
2900
 
        }
2901
 
 
2902
 
        /*
2903
 
         * If we didn't have an explicit default (which could fall in between
2904
 
         * cases, preventing us from fusing this js_SetSrcNoteOffset with the
2905
 
         * call in the loop above), link the last case to the implicit default
2906
 
         * for the decompiler.
2907
 
         */
2908
 
        if (!hasDefault &&
2909
 
            caseNoteIndex >= 0 &&
2910
 
            !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0,
2911
 
                                 CG_OFFSET(cg) - off)) {
2912
 
            return JS_FALSE;
2913
 
        }
2914
 
 
2915
 
        /* Emit default even if no explicit default statement. */
2916
 
        defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0);
2917
 
        if (defaultOffset < 0)
2918
 
            return JS_FALSE;
2919
 
    } else {
2920
 
        pc = CG_CODE(cg, top + JUMP_OFFSET_LEN);
2921
 
 
2922
 
        if (switchOp == JSOP_TABLESWITCH) {
2923
 
            /* Fill in switch bounds, which we know fit in 16-bit offsets. */
2924
 
            SET_JUMP_OFFSET(pc, low);
2925
 
            pc += JUMP_OFFSET_LEN;
2926
 
            SET_JUMP_OFFSET(pc, high);
2927
 
            pc += JUMP_OFFSET_LEN;
2928
 
 
2929
 
            /*
2930
 
             * Use malloc to avoid arena bloat for programs with many switches.
2931
 
             * We free table if non-null at label out, so all control flow must
2932
 
             * exit this function through goto out or goto bad.
2933
 
             */
2934
 
            if (tableLength != 0) {
2935
 
                tableSize = (size_t)tableLength * sizeof *table;
2936
 
                table = (JSParseNode **) JS_malloc(cx, tableSize);
2937
 
                if (!table)
2938
 
                    return JS_FALSE;
2939
 
                memset(table, 0, tableSize);
2940
 
                for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2941
 
                    if (pn3->pn_type == TOK_DEFAULT)
2942
 
                        continue;
2943
 
                    i = JSVAL_TO_INT(pn3->pn_val);
2944
 
                    i -= low;
2945
 
                    JS_ASSERT((uint32)i < tableLength);
2946
 
                    table[i] = pn3;
2947
 
                }
2948
 
            }
2949
 
        } else {
2950
 
            JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH);
2951
 
 
2952
 
            /* Fill in the number of cases. */
2953
 
            SET_ATOM_INDEX(pc, caseCount);
2954
 
            pc += ATOM_INDEX_LEN;
2955
 
        }
2956
 
 
2957
 
        /*
2958
 
         * After this point, all control flow involving JSOP_TABLESWITCH
2959
 
         * must set ok and goto out to exit this function.  To keep things
2960
 
         * simple, all switchOp cases exit that way.
2961
 
         */
2962
 
        if (constPropagated) {
2963
 
            /*
2964
 
             * Skip switchOp, as we are not setting jump offsets in the two
2965
 
             * for loops below.  We'll restore CG_NEXT(cg) from savepc after,
2966
 
             * unless there was an error.
2967
 
             */
2968
 
            savepc = CG_NEXT(cg);
2969
 
            CG_NEXT(cg) = pc + 1;
2970
 
            if (switchOp == JSOP_TABLESWITCH) {
2971
 
                for (i = 0; i < (jsint)tableLength; i++) {
2972
 
                    pn3 = table[i];
2973
 
                    if (pn3 &&
2974
 
                        (pn4 = pn3->pn_left) != NULL &&
2975
 
                        pn4->pn_type == TOK_NAME) {
2976
 
                        /* Note a propagated constant with the const's name. */
2977
 
                        JS_ASSERT(!pn4->pn_expr);
2978
 
                        ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList);
2979
 
                        if (!ale)
2980
 
                            goto bad;
2981
 
                        CG_NEXT(cg) = pc;
2982
 
                        if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t)
2983
 
                                           ALE_INDEX(ale)) < 0) {
2984
 
                            goto bad;
2985
 
                        }
2986
 
                    }
2987
 
                    pc += JUMP_OFFSET_LEN;
2988
 
                }
2989
 
            } else {
2990
 
                for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2991
 
                    pn4 = pn3->pn_left;
2992
 
                    if (pn4 && pn4->pn_type == TOK_NAME) {
2993
 
                        /* Note a propagated constant with the const's name. */
2994
 
                        JS_ASSERT(!pn4->pn_expr);
2995
 
                        ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList);
2996
 
                        if (!ale)
2997
 
                            goto bad;
2998
 
                        CG_NEXT(cg) = pc;
2999
 
                        if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t)
3000
 
                                           ALE_INDEX(ale)) < 0) {
3001
 
                            goto bad;
3002
 
                        }
3003
 
                    }
3004
 
                    pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN;
3005
 
                }
3006
 
            }
3007
 
            CG_NEXT(cg) = savepc;
3008
 
        }
3009
 
    }
3010
 
 
3011
 
    /* Emit code for each case's statements, copying pn_offset up to pn3. */
3012
 
    for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
3013
 
        if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT)
3014
 
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset);
3015
 
        pn4 = pn3->pn_right;
3016
 
        ok = js_EmitTree(cx, cg, pn4);
3017
 
        if (!ok)
3018
 
            goto out;
3019
 
        pn3->pn_offset = pn4->pn_offset;
3020
 
        if (pn3->pn_type == TOK_DEFAULT)
3021
 
            off = pn3->pn_offset - top;
3022
 
    }
3023
 
 
3024
 
    if (!hasDefault) {
3025
 
        /* If no default case, offset for default is to end of switch. */
3026
 
        off = CG_OFFSET(cg) - top;
3027
 
    }
3028
 
 
3029
 
    /* We better have set "off" by now. */
3030
 
    JS_ASSERT(off != -1);
3031
 
 
3032
 
    /* Set the default offset (to end of switch if no default). */
3033
 
    if (switchOp == JSOP_CONDSWITCH) {
3034
 
        pc = NULL;
3035
 
        JS_ASSERT(defaultOffset != -1);
3036
 
        ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset),
3037
 
                              off - (defaultOffset - top));
3038
 
        if (!ok)
3039
 
            goto out;
3040
 
    } else {
3041
 
        pc = CG_CODE(cg, top);
3042
 
        ok = js_SetJumpOffset(cx, cg, pc, off);
3043
 
        if (!ok)
3044
 
            goto out;
3045
 
        pc += JUMP_OFFSET_LEN;
3046
 
    }
3047
 
 
3048
 
    /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
3049
 
    off = CG_OFFSET(cg) - top;
3050
 
    ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off);
3051
 
    if (!ok)
3052
 
        goto out;
3053
 
 
3054
 
    if (switchOp == JSOP_TABLESWITCH) {
3055
 
        /* Skip over the already-initialized switch bounds. */
3056
 
        pc += 2 * JUMP_OFFSET_LEN;
3057
 
 
3058
 
        /* Fill in the jump table, if there is one. */
3059
 
        for (i = 0; i < (jsint)tableLength; i++) {
3060
 
            pn3 = table[i];
3061
 
            off = pn3 ? pn3->pn_offset - top : 0;
3062
 
            ok = js_SetJumpOffset(cx, cg, pc, off);
3063
 
            if (!ok)
3064
 
                goto out;
3065
 
            pc += JUMP_OFFSET_LEN;
3066
 
        }
3067
 
    } else if (switchOp == JSOP_LOOKUPSWITCH) {
3068
 
        /* Skip over the already-initialized number of cases. */
3069
 
        pc += ATOM_INDEX_LEN;
3070
 
 
3071
 
        for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
3072
 
            if (pn3->pn_type == TOK_DEFAULT)
3073
 
                continue;
3074
 
            atom = js_AtomizeValue(cx, pn3->pn_val, 0);
3075
 
            if (!atom)
3076
 
                goto bad;
3077
 
            ale = js_IndexAtom(cx, atom, &cg->atomList);
3078
 
            if (!ale)
3079
 
                goto bad;
3080
 
            SET_ATOM_INDEX(pc, ALE_INDEX(ale));
3081
 
            pc += ATOM_INDEX_LEN;
3082
 
 
3083
 
            off = pn3->pn_offset - top;
3084
 
            ok = js_SetJumpOffset(cx, cg, pc, off);
3085
 
            if (!ok)
3086
 
                goto out;
3087
 
            pc += JUMP_OFFSET_LEN;
3088
 
        }
3089
 
    }
3090
 
 
3091
 
out:
3092
 
    if (table)
3093
 
        JS_free(cx, table);
3094
 
    if (ok) {
3095
 
        ok = js_PopStatementCG(cx, cg);
3096
 
 
3097
 
#if JS_HAS_BLOCK_SCOPE
3098
 
        if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) {
3099
 
            EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
3100
 
            cg->stackDepth -= count;
3101
 
        }
3102
 
#endif
3103
 
    }
3104
 
    return ok;
3105
 
 
3106
 
bad:
3107
 
    ok = JS_FALSE;
3108
 
    goto out;
3109
 
}
3110
 
 
3111
 
JSBool
3112
 
js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
3113
 
{
3114
 
    if (!js_AllocTryNotes(cx, cg))
3115
 
        return JS_FALSE;
3116
 
 
3117
 
    if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) {
3118
 
        /* JSOP_GENERATOR must be the first instruction. */
3119
 
        CG_SWITCH_TO_PROLOG(cg);
3120
 
        JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
3121
 
        if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
3122
 
            return JS_FALSE;
3123
 
        CG_SWITCH_TO_MAIN(cg);
3124
 
    }
3125
 
 
3126
 
    return js_EmitTree(cx, cg, body) &&
3127
 
           js_Emit1(cx, cg, JSOP_STOP) >= 0;
3128
 
}
3129
 
 
3130
 
JSBool
3131
 
js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
3132
 
                    JSFunction *fun)
3133
 
{
3134
 
    JSStackFrame *fp, frame;
3135
 
    JSObject *funobj;
3136
 
    JSBool ok;
3137
 
 
3138
 
    fp = cx->fp;
3139
 
    funobj = fun->object;
3140
 
    JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
3141
 
                      fp->scopeChain != funobj));
3142
 
    memset(&frame, 0, sizeof frame);
3143
 
    frame.fun = fun;
3144
 
    frame.varobj = frame.scopeChain = funobj;
3145
 
    frame.down = fp;
3146
 
    frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
3147
 
                  ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
3148
 
                  : JSFRAME_COMPILING;
3149
 
    cx->fp = &frame;
3150
 
    ok = js_EmitFunctionBytecode(cx, cg, body);
3151
 
    cx->fp = fp;
3152
 
    if (!ok)
3153
 
        return JS_FALSE;
3154
 
 
3155
 
    if (!js_NewScriptFromCG(cx, cg, fun))
3156
 
        return JS_FALSE;
3157
 
 
3158
 
    JS_ASSERT(FUN_INTERPRETED(fun));
3159
 
    return JS_TRUE;
3160
 
}
3161
 
 
3162
 
/* A macro for inlining at the top of js_EmitTree (whence it came). */
3163
 
#define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn)                                  \
3164
 
    JS_BEGIN_MACRO                                                            \
3165
 
        uintN line_ = (pn)->pn_pos.begin.lineno;                              \
3166
 
        uintN delta_ = line_ - CG_CURRENT_LINE(cg);                           \
3167
 
        if (delta_ != 0) {                                                    \
3168
 
            /*                                                                \
3169
 
             * Encode any change in the current source line number by using   \
3170
 
             * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \
3171
 
             * whichever consumes less space.                                 \
3172
 
             *                                                                \
3173
 
             * NB: We handle backward line number deltas (possible with for   \
3174
 
             * loops where the update part is emitted after the body, but its \
3175
 
             * line number is <= any line number in the body) here by letting \
3176
 
             * unsigned delta_ wrap to a very large number, which triggers a  \
3177
 
             * SRC_SETLINE.                                                   \
3178
 
             */                                                               \
3179
 
            CG_CURRENT_LINE(cg) = line_;                                      \
3180
 
            if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \
3181
 
                if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\
3182
 
                    return JS_FALSE;                                          \
3183
 
            } else {                                                          \
3184
 
                do {                                                          \
3185
 
                    if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)               \
3186
 
                        return JS_FALSE;                                      \
3187
 
                } while (--delta_ != 0);                                      \
3188
 
            }                                                                 \
3189
 
        }                                                                     \
3190
 
    JS_END_MACRO
3191
 
 
3192
 
/* A function, so that we avoid macro-bloating all the other callsites. */
3193
 
static JSBool
3194
 
UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
3195
 
{
3196
 
    UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
3197
 
    return JS_TRUE;
3198
 
}
3199
 
 
3200
 
static JSBool
3201
 
MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
3202
 
                 JSParseNode *pn, jsatomid *result)
3203
 
{
3204
 
    jsatomid atomIndex;
3205
 
    JSAtomListElement *ale;
3206
 
 
3207
 
    if (pn->pn_slot >= 0) {
3208
 
        atomIndex = (jsatomid) pn->pn_slot;
3209
 
    } else {
3210
 
        ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
3211
 
        if (!ale)
3212
 
            return JS_FALSE;
3213
 
        atomIndex = ALE_INDEX(ale);
3214
 
    }
3215
 
 
3216
 
    if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_CONST &&
3217
 
        (!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
3218
 
         (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) {
3219
 
        /* Emit a prolog bytecode to predefine the variable. */
3220
 
        CG_SWITCH_TO_PROLOG(cg);
3221
 
        if (!UpdateLineNumberNotes(cx, cg, pn))
3222
 
            return JS_FALSE;
3223
 
        EMIT_ATOM_INDEX_OP(prologOp, atomIndex);
3224
 
        CG_SWITCH_TO_MAIN(cg);
3225
 
    }
3226
 
 
3227
 
    if (result)
3228
 
        *result = atomIndex;
3229
 
    return JS_TRUE;
3230
 
}
3231
 
 
3232
 
#if JS_HAS_DESTRUCTURING
3233
 
 
3234
 
typedef JSBool
3235
 
(*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
3236
 
                            JSParseNode *pn);
3237
 
 
3238
 
static JSBool
3239
 
EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
3240
 
                      JSParseNode *pn)
3241
 
{
3242
 
    JS_ASSERT(pn->pn_type == TOK_NAME);
3243
 
    if (!BindNameToSlot(cx, &cg->treeContext, pn, prologOp == JSOP_NOP))
3244
 
        return JS_FALSE;
3245
 
 
3246
 
    JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS);
3247
 
    return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL);
3248
 
}
3249
 
 
3250
 
static JSBool
3251
 
EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
3252
 
                       JSParseNode *pn)
3253
 
{
3254
 
    JSParseNode *pn2, *pn3;
3255
 
    DestructuringDeclEmitter emitter;
3256
 
 
3257
 
    if (pn->pn_type == TOK_RB) {
3258
 
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3259
 
            if (pn2->pn_type == TOK_COMMA)
3260
 
                continue;
3261
 
            emitter = (pn2->pn_type == TOK_NAME)
3262
 
                      ? EmitDestructuringDecl
3263
 
                      : EmitDestructuringDecls;
3264
 
            if (!emitter(cx, cg, prologOp, pn2))
3265
 
                return JS_FALSE;
3266
 
        }
3267
 
    } else {
3268
 
        JS_ASSERT(pn->pn_type == TOK_RC);
3269
 
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3270
 
            pn3 = pn2->pn_right;
3271
 
            emitter = (pn3->pn_type == TOK_NAME)
3272
 
                      ? EmitDestructuringDecl
3273
 
                      : EmitDestructuringDecls;
3274
 
            if (!emitter(cx, cg, prologOp, pn3))
3275
 
                return JS_FALSE;
3276
 
        }
3277
 
    }
3278
 
    return JS_TRUE;
3279
 
}
3280
 
 
3281
 
static JSBool
3282
 
EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn);
3283
 
 
3284
 
static JSBool
3285
 
EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
3286
 
                     JSBool wantpop)
3287
 
{
3288
 
    jsuint slot;
3289
 
 
3290
 
    /* Skip any parenthesization. */
3291
 
    while (pn->pn_type == TOK_RP)
3292
 
        pn = pn->pn_kid;
3293
 
 
3294
 
    /*
3295
 
     * Now emit the lvalue opcode sequence.  If the lvalue is a nested
3296
 
     * destructuring initialiser-form, call ourselves to handle it, then
3297
 
     * pop the matched value.  Otherwise emit an lvalue bytecode sequence
3298
 
     * ending with a JSOP_ENUMELEM or equivalent op.
3299
 
     */
3300
 
    if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
3301
 
        if (!EmitDestructuringOpsHelper(cx, cg, pn))
3302
 
            return JS_FALSE;
3303
 
        if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0)
3304
 
            return JS_FALSE;
3305
 
    } else {
3306
 
        if (pn->pn_type == TOK_NAME &&
3307
 
            !BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) {
3308
 
            return JS_FALSE;
3309
 
        }
3310
 
 
3311
 
        switch (pn->pn_op) {
3312
 
          case JSOP_SETNAME:
3313
 
            /*
3314
 
             * NB: pn is a PN_NAME node, not a PN_BINARY.  Nevertheless,
3315
 
             * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM.
3316
 
             * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp.
3317
 
             */
3318
 
            if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg))
3319
 
                return JS_FALSE;
3320
 
            break;
3321
 
 
3322
 
          case JSOP_SETCONST:
3323
 
            if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg))
3324
 
                return JS_FALSE;
3325
 
            break;
3326
 
 
3327
 
          case JSOP_SETLOCAL:
3328
 
            if (wantpop) {
3329
 
                slot = (jsuint) pn->pn_slot;
3330
 
                EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot);
3331
 
                break;
3332
 
            }
3333
 
            /* FALL THROUGH */
3334
 
 
3335
 
          case JSOP_SETARG:
3336
 
          case JSOP_SETVAR:
3337
 
          case JSOP_SETGVAR:
3338
 
            slot = (jsuint) pn->pn_slot;
3339
 
            EMIT_UINT16_IMM_OP(pn->pn_op, slot);
3340
 
            if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0)
3341
 
                return JS_FALSE;
3342
 
            break;
3343
 
 
3344
 
          default:
3345
 
#if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT
3346
 
          {
3347
 
            ptrdiff_t top;
3348
 
 
3349
 
            top = CG_OFFSET(cg);
3350
 
            if (!js_EmitTree(cx, cg, pn))
3351
 
                return JS_FALSE;
3352
 
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
3353
 
                return JS_FALSE;
3354
 
            if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0)
3355
 
                return JS_FALSE;
3356
 
            break;
3357
 
          }
3358
 
#endif
3359
 
          case JSOP_ENUMELEM:
3360
 
            JS_ASSERT(0);
3361
 
        }
3362
 
    }
3363
 
 
3364
 
    return JS_TRUE;
3365
 
}
3366
 
 
3367
 
/*
3368
 
 * Recursive helper for EmitDestructuringOps.
3369
 
 *
3370
 
 * Given a value to destructure on the stack, walk over an object or array
3371
 
 * initialiser at pn, emitting bytecodes to match property values and store
3372
 
 * them in the lvalues identified by the matched property names.
3373
 
 */
3374
 
static JSBool
3375
 
EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
3376
 
{
3377
 
    jsuint index;
3378
 
    JSParseNode *pn2, *pn3;
3379
 
    JSBool doElemOp;
3380
 
 
3381
 
#ifdef DEBUG
3382
 
    intN stackDepth = cg->stackDepth;
3383
 
    JS_ASSERT(stackDepth != 0);
3384
 
    JS_ASSERT(pn->pn_arity == PN_LIST);
3385
 
    JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC);
3386
 
#endif
3387
 
 
3388
 
    if (pn->pn_count == 0) {
3389
 
        /* Emit a DUP;POP sequence for the decompiler. */
3390
 
        return js_Emit1(cx, cg, JSOP_DUP) >= 0 &&
3391
 
               js_Emit1(cx, cg, JSOP_POP) >= 0;
3392
 
    }
3393
 
 
3394
 
    index = 0;
3395
 
    for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3396
 
        /*
3397
 
         * Duplicate the value being destructured to use as a reference base.
3398
 
         */
3399
 
        if (js_Emit1(cx, cg, JSOP_DUP) < 0)
3400
 
            return JS_FALSE;
3401
 
 
3402
 
        /*
3403
 
         * Now push the property name currently being matched, which is either
3404
 
         * the array initialiser's current index, or the current property name
3405
 
         * "label" on the left of a colon in the object initialiser.  Set pn3
3406
 
         * to the lvalue node, which is in the value-initializing position.
3407
 
         */
3408
 
        doElemOp = JS_TRUE;
3409
 
        if (pn->pn_type == TOK_RB) {
3410
 
            if (!EmitNumberOp(cx, index, cg))
3411
 
                return JS_FALSE;
3412
 
            pn3 = pn2;
3413
 
        } else {
3414
 
            JS_ASSERT(pn->pn_type == TOK_RC);
3415
 
            JS_ASSERT(pn2->pn_type == TOK_COLON);
3416
 
            pn3 = pn2->pn_left;
3417
 
            if (pn3->pn_type == TOK_NUMBER) {
3418
 
                /*
3419
 
                 * If we are emitting an object destructuring initialiser,
3420
 
                 * annotate the index op with SRC_INITPROP so we know we are
3421
 
                 * not decompiling an array initialiser.
3422
 
                 */
3423
 
                if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
3424
 
                    return JS_FALSE;
3425
 
                if (!EmitNumberOp(cx, pn3->pn_dval, cg))
3426
 
                    return JS_FALSE;
3427
 
            } else {
3428
 
                JS_ASSERT(pn3->pn_type == TOK_STRING ||
3429
 
                          pn3->pn_type == TOK_NAME);
3430
 
                if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg))
3431
 
                    return JS_FALSE;
3432
 
                doElemOp = JS_FALSE;
3433
 
            }
3434
 
            pn3 = pn2->pn_right;
3435
 
        }
3436
 
 
3437
 
        if (doElemOp) {
3438
 
            /*
3439
 
             * Ok, get the value of the matching property name.  This leaves
3440
 
             * that value on top of the value being destructured, so the stack
3441
 
             * is one deeper than when we started.
3442
 
             */
3443
 
            if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
3444
 
                return JS_FALSE;
3445
 
            JS_ASSERT(cg->stackDepth == stackDepth + 1);
3446
 
        }
3447
 
 
3448
 
        /* Nullary comma node makes a hole in the array destructurer. */
3449
 
        if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) {
3450
 
            JS_ASSERT(pn->pn_type == TOK_RB);
3451
 
            JS_ASSERT(pn2 == pn3);
3452
 
            if (js_Emit1(cx, cg, JSOP_POP) < 0)
3453
 
                return JS_FALSE;
3454
 
        } else {
3455
 
            if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE))
3456
 
                return JS_FALSE;
3457
 
        }
3458
 
 
3459
 
        JS_ASSERT(cg->stackDepth == stackDepth);
3460
 
        ++index;
3461
 
    }
3462
 
 
3463
 
    return JS_TRUE;
3464
 
}
3465
 
 
3466
 
static ptrdiff_t
3467
 
OpToDeclType(JSOp op)
3468
 
{
3469
 
    switch (op) {
3470
 
      case JSOP_NOP:
3471
 
        return SRC_DECL_LET;
3472
 
      case JSOP_DEFCONST:
3473
 
        return SRC_DECL_CONST;
3474
 
      case JSOP_DEFVAR:
3475
 
        return SRC_DECL_VAR;
3476
 
      default:
3477
 
        return SRC_DECL_NONE;
3478
 
    }
3479
 
}
3480
 
 
3481
 
static JSBool
3482
 
EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
3483
 
                     JSParseNode *pn)
3484
 
{
3485
 
    /*
3486
 
     * If we're called from a variable declaration, help the decompiler by
3487
 
     * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits.
3488
 
     * If the destructuring initialiser is empty, our helper will emit a
3489
 
     * JSOP_DUP followed by a JSOP_POP for the decompiler.
3490
 
     */
3491
 
    if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0)
3492
 
        return JS_FALSE;
3493
 
 
3494
 
    /*
3495
 
     * Call our recursive helper to emit the destructuring assignments and
3496
 
     * related stack manipulations.
3497
 
     */
3498
 
    return EmitDestructuringOpsHelper(cx, cg, pn);
3499
 
}
3500
 
 
3501
 
static JSBool
3502
 
EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
3503
 
                    JSParseNode *lhs, JSParseNode *rhs)
3504
 
{
3505
 
    jsuint depth, limit, slot;
3506
 
    JSParseNode *pn;
3507
 
 
3508
 
    depth = limit = (uintN) cg->stackDepth;
3509
 
    for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
3510
 
        if (limit == JS_BIT(16)) {
3511
 
            js_ReportCompileErrorNumber(cx, rhs,
3512
 
                                        JSREPORT_PN | JSREPORT_ERROR,
3513
 
                                        JSMSG_ARRAY_INIT_TOO_BIG);
3514
 
            return JS_FALSE;
3515
 
        }
3516
 
 
3517
 
        if (pn->pn_type == TOK_COMMA) {
3518
 
            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
3519
 
                return JS_FALSE;
3520
 
        } else {
3521
 
            JS_ASSERT(pn->pn_type != TOK_DEFSHARP);
3522
 
            if (!js_EmitTree(cx, cg, pn))
3523
 
                return JS_FALSE;
3524
 
        }
3525
 
        ++limit;
3526
 
    }
3527
 
 
3528
 
    if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0)
3529
 
        return JS_FALSE;
3530
 
 
3531
 
    slot = depth;
3532
 
    for (pn = lhs->pn_head; pn; pn = pn->pn_next) {
3533
 
        if (slot < limit) {
3534
 
            EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot);
3535
 
        } else {
3536
 
            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
3537
 
                return JS_FALSE;
3538
 
        }
3539
 
        if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) {
3540
 
            if (js_Emit1(cx, cg, JSOP_POP) < 0)
3541
 
                return JS_FALSE;
3542
 
        } else {
3543
 
            if (!EmitDestructuringLHS(cx, cg, pn, pn->pn_next != NULL))
3544
 
                return JS_FALSE;
3545
 
        }
3546
 
        ++slot;
3547
 
    }
3548
 
 
3549
 
    EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
3550
 
    cg->stackDepth = (uintN) depth;
3551
 
    return JS_TRUE;
3552
 
}
3553
 
 
3554
 
/*
3555
 
 * Helper called with pop out param initialized to a JSOP_POP* opcode.  If we
3556
 
 * can emit a group assignment sequence, which results in 0 stack depth delta,
3557
 
 * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop.
3558
 
 */
3559
 
static JSBool
3560
 
MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
3561
 
                         JSParseNode *pn, JSOp *pop)
3562
 
{
3563
 
    JSParseNode *lhs, *rhs;
3564
 
 
3565
 
    JS_ASSERT(pn->pn_type == TOK_ASSIGN);
3566
 
    JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
3567
 
    lhs = pn->pn_left;
3568
 
    rhs = pn->pn_right;
3569
 
    if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB &&
3570
 
        lhs->pn_count <= rhs->pn_count &&
3571
 
        (rhs->pn_count == 0 ||
3572
 
         rhs->pn_head->pn_type != TOK_DEFSHARP)) {
3573
 
        if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs))
3574
 
            return JS_FALSE;
3575
 
        *pop = JSOP_NOP;
3576
 
    }
3577
 
    return JS_TRUE;
3578
 
}
3579
 
 
3580
 
#endif /* JS_HAS_DESTRUCTURING */
3581
 
 
3582
 
static JSBool
3583
 
EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
3584
 
              JSBool inLetHead, ptrdiff_t *headNoteIndex)
3585
 
{
3586
 
    JSTreeContext *tc;
3587
 
    JSBool let, forInVar;
3588
 
#if JS_HAS_BLOCK_SCOPE
3589
 
    JSBool forInLet, popScope;
3590
 
    JSStmtInfo *stmt, *scopeStmt;
3591
 
#endif
3592
 
    ptrdiff_t off, noteIndex, tmp;
3593
 
    JSParseNode *pn2, *pn3;
3594
 
    JSOp op;
3595
 
    jsatomid atomIndex;
3596
 
    uintN oldflags;
3597
 
 
3598
 
    /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */
3599
 
    *headNoteIndex = -1;
3600
 
 
3601
 
    /*
3602
 
     * Let blocks and expressions have a parenthesized head in which the new
3603
 
     * scope is not yet open. Initializer evaluation uses the parent node's
3604
 
     * lexical scope. If popScope is true below, then we hide the top lexical
3605
 
     * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that
3606
 
     * it won't find any names in the new let block.
3607
 
     *
3608
 
     * The same goes for let declarations in the head of any kind of for loop.
3609
 
     * Unlike a let declaration 'let x = i' within a block, where x is hoisted
3610
 
     * to the start of the block, a 'for (let x = i...) ...' loop evaluates i
3611
 
     * in the containing scope, and puts x in the loop body's scope.
3612
 
     */
3613
 
    tc = &cg->treeContext;
3614
 
    let = (pn->pn_op == JSOP_NOP);
3615
 
    forInVar = (pn->pn_extra & PNX_FORINVAR) != 0;
3616
 
#if JS_HAS_BLOCK_SCOPE
3617
 
    forInLet = let && forInVar;
3618
 
    popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
3619
 
    JS_ASSERT(!popScope || let);
3620
 
#endif
3621
 
 
3622
 
    off = noteIndex = -1;
3623
 
    for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
3624
 
#if JS_HAS_DESTRUCTURING
3625
 
        if (pn2->pn_type != TOK_NAME) {
3626
 
            if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) {
3627
 
                /*
3628
 
                 * Emit variable binding ops, but not destructuring ops.
3629
 
                 * The parser (see Variables, jsparse.c) has ensured that
3630
 
                 * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree,
3631
 
                 * and that case will emit the destructuring code only after
3632
 
                 * emitting an enumerating opcode and a branch that tests
3633
 
                 * whether the enumeration ended.
3634
 
                 */
3635
 
                JS_ASSERT(forInVar);
3636
 
                JS_ASSERT(pn->pn_count == 1);
3637
 
                if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn2))
3638
 
                    return JS_FALSE;
3639
 
                break;
3640
 
            }
3641
 
 
3642
 
            /*
3643
 
             * A destructuring initialiser assignment preceded by var is
3644
 
             * always evaluated promptly, even if it is to the left of 'in'
3645
 
             * in a for-in loop.  As with 'for (var x = i in o)...', this
3646
 
             * will cause the entire 'var [a, b] = i' to be hoisted out of
3647
 
             * the head of the loop.
3648
 
             */
3649
 
            JS_ASSERT(pn2->pn_type == TOK_ASSIGN);
3650
 
            if (pn->pn_count == 1 && !forInLet) {
3651
 
                /*
3652
 
                 * If this is the only destructuring assignment in the list,
3653
 
                 * try to optimize to a group assignment.  If we're in a let
3654
 
                 * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP
3655
 
                 * in pn->pn_op, to suppress a second (and misplaced) 'let'.
3656
 
                 */
3657
 
                JS_ASSERT(noteIndex < 0 && !pn2->pn_next);
3658
 
                op = JSOP_POP;
3659
 
                if (!MaybeEmitGroupAssignment(cx, cg,
3660
 
                                              inLetHead ? JSOP_POP : pn->pn_op,
3661
 
                                              pn2, &op)) {
3662
 
                    return JS_FALSE;
3663
 
                }
3664
 
                if (op == JSOP_NOP) {
3665
 
                    pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT;
3666
 
                    break;
3667
 
                }
3668
 
            }
3669
 
 
3670
 
            pn3 = pn2->pn_left;
3671
 
            if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn3))
3672
 
                return JS_FALSE;
3673
 
 
3674
 
#if JS_HAS_BLOCK_SCOPE
3675
 
            /*
3676
 
             * If this is a 'for (let [x, y] = i in o) ...' let declaration,
3677
 
             * throw away i if it is a useless expression.
3678
 
             */
3679
 
            if (forInLet) {
3680
 
                JSBool useful = JS_FALSE;
3681
 
 
3682
 
                JS_ASSERT(pn->pn_count == 1);
3683
 
                if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful))
3684
 
                    return JS_FALSE;
3685
 
                if (!useful)
3686
 
                    return JS_TRUE;
3687
 
            }
3688
 
#endif
3689
 
 
3690
 
            if (!js_EmitTree(cx, cg, pn2->pn_right))
3691
 
                return JS_FALSE;
3692
 
 
3693
 
#if JS_HAS_BLOCK_SCOPE
3694
 
            /*
3695
 
             * The expression i in 'for (let [x, y] = i in o) ...', which is
3696
 
             * pn2->pn_right above, appears to have side effects.  We've just
3697
 
             * emitted code to evaluate i, but we must not destructure i yet.
3698
 
             * Let the TOK_FOR: code in js_EmitTree do the destructuring to
3699
 
             * emit the right combination of source notes and bytecode for the
3700
 
             * decompiler.
3701
 
             *
3702
 
             * This has the effect of hoisting the evaluation of i out of the
3703
 
             * for-in loop, without hoisting the let variables, which must of
3704
 
             * course be scoped by the loop.  Set PNX_POPVAR to cause JSOP_POP
3705
 
             * to be emitted, just before returning from this function.
3706
 
             */
3707
 
            if (forInVar) {
3708
 
                pn->pn_extra |= PNX_POPVAR;
3709
 
                if (forInLet)
3710
 
                    break;
3711
 
            }
3712
 
#endif
3713
 
 
3714
 
            /*
3715
 
             * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT
3716
 
             * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that
3717
 
             * we will emit at the bottom of this function.
3718
 
             */
3719
 
            if (!EmitDestructuringOps(cx, cg,
3720
 
                                      inLetHead ? JSOP_POP : pn->pn_op,
3721
 
                                      pn3)) {
3722
 
                return JS_FALSE;
3723
 
            }
3724
 
            goto emit_note_pop;
3725
 
        }
3726
 
#else
3727
 
        JS_ASSERT(pn2->pn_type == TOK_NAME);
3728
 
#endif
3729
 
 
3730
 
        if (!BindNameToSlot(cx, &cg->treeContext, pn2, let))
3731
 
            return JS_FALSE;
3732
 
        JS_ASSERT(pn2->pn_slot >= 0 || !let);
3733
 
 
3734
 
        op = pn2->pn_op;
3735
 
        if (op == JSOP_ARGUMENTS) {
3736
 
            /* JSOP_ARGUMENTS => no initializer */
3737
 
            JS_ASSERT(!pn2->pn_expr && !let);
3738
 
            pn3 = NULL;
3739
 
#ifdef __GNUC__
3740
 
            atomIndex = 0;            /* quell GCC overwarning */
3741
 
#endif
3742
 
        } else {
3743
 
            if (!MaybeEmitVarDecl(cx, cg, pn->pn_op, pn2, &atomIndex))
3744
 
                return JS_FALSE;
3745
 
 
3746
 
            pn3 = pn2->pn_expr;
3747
 
            if (pn3) {
3748
 
#if JS_HAS_BLOCK_SCOPE
3749
 
                /*
3750
 
                 * If this is a 'for (let x = i in o) ...' let declaration,
3751
 
                 * throw away i if it is a useless expression.
3752
 
                 */
3753
 
                if (forInLet) {
3754
 
                    JSBool useful = JS_FALSE;
3755
 
 
3756
 
                    JS_ASSERT(pn->pn_count == 1);
3757
 
                    if (!CheckSideEffects(cx, tc, pn3, &useful))
3758
 
                        return JS_FALSE;
3759
 
                    if (!useful)
3760
 
                        return JS_TRUE;
3761
 
                }
3762
 
#endif
3763
 
 
3764
 
                if (op == JSOP_SETNAME) {
3765
 
                    JS_ASSERT(!let);
3766
 
                    EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
3767
 
                }
3768
 
                if (pn->pn_op == JSOP_DEFCONST &&
3769
 
                    !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
3770
 
                                                  pn3)) {
3771
 
                    return JS_FALSE;
3772
 
                }
3773
 
 
3774
 
#if JS_HAS_BLOCK_SCOPE
3775
 
                /* Evaluate expr in the outer lexical scope if requested. */
3776
 
                if (popScope) {
3777
 
                    stmt = tc->topStmt;
3778
 
                    scopeStmt = tc->topScopeStmt;
3779
 
 
3780
 
                    tc->topStmt = stmt->down;
3781
 
                    tc->topScopeStmt = scopeStmt->downScope;
3782
 
                }
3783
 
#ifdef __GNUC__
3784
 
                else {
3785
 
                    stmt = scopeStmt = NULL;    /* quell GCC overwarning */
3786
 
                }
3787
 
#endif
3788
 
#endif
3789
 
 
3790
 
                oldflags = cg->treeContext.flags;
3791
 
                cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
3792
 
                if (!js_EmitTree(cx, cg, pn3))
3793
 
                    return JS_FALSE;
3794
 
                cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
3795
 
 
3796
 
#if JS_HAS_BLOCK_SCOPE
3797
 
                if (popScope) {
3798
 
                    tc->topStmt = stmt;
3799
 
                    tc->topScopeStmt = scopeStmt;
3800
 
                }
3801
 
#endif
3802
 
            }
3803
 
        }
3804
 
 
3805
 
        /*
3806
 
         * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the
3807
 
         * TOK_VAR case, but only the initialized case (a strange one that
3808
 
         * falls out of ECMA-262's grammar) wants to run past this point.
3809
 
         * Both cases must conditionally emit a JSOP_DEFVAR, above.  Note
3810
 
         * that the parser error-checks to ensure that pn->pn_count is 1.
3811
 
         *
3812
 
         * 'for (let x = i in o) ...' must evaluate i before the loop, and
3813
 
         * subject it to useless expression elimination.  The variable list
3814
 
         * in pn is a single let declaration if pn_op == JSOP_NOP.  We test
3815
 
         * the let local in order to break early in this case, as well as in
3816
 
         * the 'for (var x in o)' case.
3817
 
         *
3818
 
         * XXX Narcissus keeps track of variable declarations in the node
3819
 
         * for the script being compiled, so there's no need to share any
3820
 
         * conditional prolog code generation there.  We could do likewise,
3821
 
         * but it's a big change, requiring extra allocation, so probably
3822
 
         * not worth the trouble for SpiderMonkey.
3823
 
         */
3824
 
        JS_ASSERT(pn3 == pn2->pn_expr);
3825
 
        if (forInVar && (!pn3 || let)) {
3826
 
            JS_ASSERT(pn->pn_count == 1);
3827
 
            break;
3828
 
        }
3829
 
 
3830
 
        if (pn2 == pn->pn_head &&
3831
 
            !inLetHead &&
3832
 
            js_NewSrcNote2(cx, cg, SRC_DECL,
3833
 
                           (pn->pn_op == JSOP_DEFCONST)
3834
 
                           ? SRC_DECL_CONST
3835
 
                           : (pn->pn_op == JSOP_DEFVAR)
3836
 
                           ? SRC_DECL_VAR
3837
 
                           : SRC_DECL_LET) < 0) {
3838
 
            return JS_FALSE;
3839
 
        }
3840
 
        if (op == JSOP_ARGUMENTS) {
3841
 
            if (js_Emit1(cx, cg, op) < 0)
3842
 
                return JS_FALSE;
3843
 
        } else if (pn2->pn_slot >= 0) {
3844
 
            EMIT_UINT16_IMM_OP(op, atomIndex);
3845
 
        } else {
3846
 
            EMIT_ATOM_INDEX_OP(op, atomIndex);
3847
 
        }
3848
 
 
3849
 
#if JS_HAS_DESTRUCTURING
3850
 
    emit_note_pop:
3851
 
#endif
3852
 
        tmp = CG_OFFSET(cg);
3853
 
        if (noteIndex >= 0) {
3854
 
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
3855
 
                return JS_FALSE;
3856
 
        }
3857
 
        if (!pn2->pn_next)
3858
 
            break;
3859
 
        off = tmp;
3860
 
        noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
3861
 
        if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
3862
 
            return JS_FALSE;
3863
 
    }
3864
 
 
3865
 
    /* If this is a let head, emit and return a srcnote on the pop. */
3866
 
    if (inLetHead) {
3867
 
        *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL);
3868
 
        if (*headNoteIndex < 0)
3869
 
            return JS_FALSE;
3870
 
        if (!(pn->pn_extra & PNX_POPVAR))
3871
 
            return js_Emit1(cx, cg, JSOP_NOP) >= 0;
3872
 
    }
3873
 
 
3874
 
    return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0;
3875
 
}
3876
 
 
3877
 
#if defined DEBUG_brendan || defined DEBUG_mrbkap
3878
 
static JSBool
3879
 
GettableNoteForNextOp(JSCodeGenerator *cg)
3880
 
{
3881
 
    ptrdiff_t offset, target;
3882
 
    jssrcnote *sn, *end;
3883
 
 
3884
 
    offset = 0;
3885
 
    target = CG_OFFSET(cg);
3886
 
    for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end;
3887
 
         sn = SN_NEXT(sn)) {
3888
 
        if (offset == target && SN_IS_GETTABLE(sn))
3889
 
            return JS_TRUE;
3890
 
        offset += SN_DELTA(sn);
3891
 
    }
3892
 
    return JS_FALSE;
3893
 
}
3894
 
#endif
3895
 
 
3896
 
JSBool
3897
 
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
3898
 
{
3899
 
    JSBool ok, useful, wantval;
3900
 
    JSStmtInfo *stmt, stmtInfo;
3901
 
    ptrdiff_t top, off, tmp, beq, jmp;
3902
 
    JSParseNode *pn2, *pn3;
3903
 
    JSAtom *atom;
3904
 
    JSAtomListElement *ale;
3905
 
    jsatomid atomIndex;
3906
 
    ptrdiff_t noteIndex;
3907
 
    JSSrcNoteType noteType;
3908
 
    jsbytecode *pc;
3909
 
    JSOp op;
3910
 
    JSTokenType type;
3911
 
    uint32 argc;
3912
 
    int stackDummy;
3913
 
 
3914
 
    if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
3915
 
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
3916
 
        return JS_FALSE;
3917
 
    }
3918
 
 
3919
 
    ok = JS_TRUE;
3920
 
    cg->emitLevel++;
3921
 
    pn->pn_offset = top = CG_OFFSET(cg);
3922
 
 
3923
 
    /* Emit notes to tell the current bytecode's source line number. */
3924
 
    UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
3925
 
 
3926
 
    switch (pn->pn_type) {
3927
 
      case TOK_FUNCTION:
3928
 
      {
3929
 
        void *cg2mark;
3930
 
        JSCodeGenerator *cg2;
3931
 
        JSFunction *fun;
3932
 
 
3933
 
#if JS_HAS_XML_SUPPORT
3934
 
        if (pn->pn_arity == PN_NULLARY) {
3935
 
            if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0)
3936
 
                return JS_FALSE;
3937
 
            break;
3938
 
        }
3939
 
#endif
3940
 
 
3941
 
        /* Generate code for the function's body. */
3942
 
        cg2mark = JS_ARENA_MARK(&cx->tempPool);
3943
 
        JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool);
3944
 
        if (!cg2) {
3945
 
            JS_ReportOutOfMemory(cx);
3946
 
            return JS_FALSE;
3947
 
        }
3948
 
        if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool,
3949
 
                                  cg->filename, pn->pn_pos.begin.lineno,
3950
 
                                  cg->principals)) {
3951
 
            return JS_FALSE;
3952
 
        }
3953
 
        cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
3954
 
        cg2->treeContext.tryCount = pn->pn_tryCount;
3955
 
        cg2->parent = cg;
3956
 
        fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
3957
 
        if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun))
3958
 
            return JS_FALSE;
3959
 
 
3960
 
        /*
3961
 
         * We need an activation object if an inner peeks out, or if such
3962
 
         * inner-peeking caused one of our inners to become heavyweight.
3963
 
         */
3964
 
        if (cg2->treeContext.flags &
3965
 
            (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {
3966
 
            cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
3967
 
        }
3968
 
        js_FinishCodeGenerator(cx, cg2);
3969
 
        JS_ARENA_RELEASE(&cx->tempPool, cg2mark);
3970
 
 
3971
 
        /* Make the function object a literal in the outer script's pool. */
3972
 
        ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList);
3973
 
        if (!ale)
3974
 
            return JS_FALSE;
3975
 
        atomIndex = ALE_INDEX(ale);
3976
 
 
3977
 
        /* Emit a bytecode pointing to the closure object in its immediate. */
3978
 
        if (pn->pn_op != JSOP_NOP) {
3979
 
            EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
3980
 
            break;
3981
 
        }
3982
 
 
3983
 
        /* Top-level named functions need a nop for decompilation. */
3984
 
        noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);
3985
 
        if (noteIndex < 0 ||
3986
 
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
3987
 
            return JS_FALSE;
3988
 
        }
3989
 
 
3990
 
        /*
3991
 
         * Top-levels also need a prolog op to predefine their names in the
3992
 
         * variable object, or if local, to fill their stack slots.
3993
 
         */
3994
 
        CG_SWITCH_TO_PROLOG(cg);
3995
 
 
3996
 
        if (cg->treeContext.flags & TCF_IN_FUNCTION) {
3997
 
            JSObject *obj, *pobj;
3998
 
            JSProperty *prop;
3999
 
            JSScopeProperty *sprop;
4000
 
            uintN slot;
4001
 
 
4002
 
            obj = OBJ_GET_PARENT(cx, fun->object);
4003
 
            if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom),
4004
 
                                         &pobj, &prop)) {
4005
 
                return JS_FALSE;
4006
 
            }
4007
 
 
4008
 
            JS_ASSERT(prop && pobj == obj);
4009
 
            sprop = (JSScopeProperty *) prop;
4010
 
            JS_ASSERT(sprop->getter == js_GetLocalVariable);
4011
 
            slot = sprop->shortid;
4012
 
            OBJ_DROP_PROPERTY(cx, pobj, prop);
4013
 
 
4014
 
            /*
4015
 
             * If this local function is declared in a body block induced by
4016
 
             * let declarations, reparent fun->object to the compiler-created
4017
 
             * body block object so that JSOP_DEFLOCALFUN can clone that block
4018
 
             * into the runtime scope chain.
4019
 
             */
4020
 
            stmt = cg->treeContext.topStmt;
4021
 
            if (stmt && stmt->type == STMT_BLOCK &&
4022
 
                stmt->down && stmt->down->type == STMT_BLOCK &&
4023
 
                (stmt->down->flags & SIF_SCOPE)) {
4024
 
                obj = ATOM_TO_OBJECT(stmt->down->atom);
4025
 
                JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
4026
 
                OBJ_SET_PARENT(cx, fun->object, obj);
4027
 
            }
4028
 
 
4029
 
            if (atomIndex >= JS_BIT(16)) {
4030
 
                /*
4031
 
                 * Lots of literals in the outer function, so we have to emit
4032
 
                 * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot].
4033
 
                 */
4034
 
                off = js_EmitN(cx, cg, JSOP_LITOPX, 3);
4035
 
                if (off < 0)
4036
 
                    return JS_FALSE;
4037
 
                pc = CG_CODE(cg, off);
4038
 
                SET_LITERAL_INDEX(pc, atomIndex);
4039
 
                EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot);
4040
 
            } else {
4041
 
                /* Emit [JSOP_DEFLOCALFUN, var slot, atomIndex]. */
4042
 
                off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN,
4043
 
                               VARNO_LEN + ATOM_INDEX_LEN);
4044
 
                if (off < 0)
4045
 
                    return JS_FALSE;
4046
 
                pc = CG_CODE(cg, off);
4047
 
                SET_VARNO(pc, slot);
4048
 
                pc += VARNO_LEN;
4049
 
                SET_ATOM_INDEX(pc, atomIndex);
4050
 
            }
4051
 
        } else {
4052
 
            JS_ASSERT(!cg->treeContext.topStmt);
4053
 
            EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
4054
 
        }
4055
 
 
4056
 
        CG_SWITCH_TO_MAIN(cg);
4057
 
        break;
4058
 
      }
4059
 
 
4060
 
#if JS_HAS_EXPORT_IMPORT
4061
 
      case TOK_EXPORT:
4062
 
        pn2 = pn->pn_head;
4063
 
        if (pn2->pn_type == TOK_STAR) {
4064
 
            /*
4065
 
             * 'export *' must have no other elements in the list (what would
4066
 
             * be the point?).
4067
 
             */
4068
 
            if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0)
4069
 
                return JS_FALSE;
4070
 
        } else {
4071
 
            /*
4072
 
             * If not 'export *', the list consists of NAME nodes identifying
4073
 
             * properties of the variables object to flag as exported.
4074
 
             */
4075
 
            do {
4076
 
                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
4077
 
                if (!ale)
4078
 
                    return JS_FALSE;
4079
 
                EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale));
4080
 
            } while ((pn2 = pn2->pn_next) != NULL);
4081
 
        }
4082
 
        break;
4083
 
 
4084
 
      case TOK_IMPORT:
4085
 
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
4086
 
            /*
4087
 
             * Each subtree on an import list is rooted by a DOT or LB node.
4088
 
             * A DOT may have a null pn_atom member, in which case pn_op must
4089
 
             * be JSOP_IMPORTALL -- see EmitPropOp above.
4090
 
             */
4091
 
            if (!js_EmitTree(cx, cg, pn2))
4092
 
                return JS_FALSE;
4093
 
        }
4094
 
        break;
4095
 
#endif /* JS_HAS_EXPORT_IMPORT */
4096
 
 
4097
 
      case TOK_IF:
4098
 
        /* Initialize so we can detect else-if chains and avoid recursion. */
4099
 
        stmtInfo.type = STMT_IF;
4100
 
        beq = jmp = -1;
4101
 
        noteIndex = -1;
4102
 
 
4103
 
      if_again:
4104
 
        /* Emit code for the condition before pushing stmtInfo. */
4105
 
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
4106
 
            return JS_FALSE;
4107
 
        top = CG_OFFSET(cg);
4108
 
        if (stmtInfo.type == STMT_IF) {
4109
 
            js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top);
4110
 
        } else {
4111
 
            /*
4112
 
             * We came here from the goto further below that detects else-if
4113
 
             * chains, so we must mutate stmtInfo back into a STMT_IF record.
4114
 
             * Also (see below for why) we need a note offset for SRC_IF_ELSE
4115
 
             * to help the decompiler.  Actually, we need two offsets, one for
4116
 
             * decompiling any else clause and the second for decompiling an
4117
 
             * else-if chain without bracing, overindenting, or incorrectly
4118
 
             * scoping let declarations.
4119
 
             */
4120
 
            JS_ASSERT(stmtInfo.type == STMT_ELSE);
4121
 
            stmtInfo.type = STMT_IF;
4122
 
            stmtInfo.update = top;
4123
 
            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
4124
 
                return JS_FALSE;
4125
 
            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp))
4126
 
                return JS_FALSE;
4127
 
        }
4128
 
 
4129
 
        /* Emit an annotated branch-if-false around the then part. */
4130
 
        pn3 = pn->pn_kid3;
4131
 
        noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
4132
 
        if (noteIndex < 0)
4133
 
            return JS_FALSE;
4134
 
        beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
4135
 
        if (beq < 0)
4136
 
            return JS_FALSE;
4137
 
 
4138
 
        /* Emit code for the then and optional else parts. */
4139
 
        if (!js_EmitTree(cx, cg, pn->pn_kid2))
4140
 
            return JS_FALSE;
4141
 
        if (pn3) {
4142
 
            /* Modify stmtInfo so we know we're in the else part. */
4143
 
            stmtInfo.type = STMT_ELSE;
4144
 
 
4145
 
            /*
4146
 
             * Emit a JSOP_BACKPATCH op to jump from the end of our then part
4147
 
             * around the else part.  The js_PopStatementCG call at the bottom
4148
 
             * of this switch case will fix up the backpatch chain linked from
4149
 
             * stmtInfo.breaks.
4150
 
             */
4151
 
            jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL);
4152
 
            if (jmp < 0)
4153
 
                return JS_FALSE;
4154
 
 
4155
 
            /* Ensure the branch-if-false comes here, then emit the else. */
4156
 
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
4157
 
            if (pn3->pn_type == TOK_IF) {
4158
 
                pn = pn3;
4159
 
                goto if_again;
4160
 
            }
4161
 
 
4162
 
            if (!js_EmitTree(cx, cg, pn3))
4163
 
                return JS_FALSE;
4164
 
 
4165
 
            /*
4166
 
             * Annotate SRC_IF_ELSE with the offset from branch to jump, for
4167
 
             * the decompiler's benefit.  We can't just "back up" from the pc
4168
 
             * of the else clause, because we don't know whether an extended
4169
 
             * jump was required to leap from the end of the then clause over
4170
 
             * the else clause.
4171
 
             */
4172
 
            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
4173
 
                return JS_FALSE;
4174
 
        } else {
4175
 
            /* No else part, fixup the branch-if-false to come here. */
4176
 
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
4177
 
        }
4178
 
        ok = js_PopStatementCG(cx, cg);
4179
 
        break;
4180
 
 
4181
 
      case TOK_SWITCH:
4182
 
        /* Out of line to avoid bloating js_EmitTree's stack frame size. */
4183
 
        ok = EmitSwitch(cx, cg, pn, &stmtInfo);
4184
 
        break;
4185
 
 
4186
 
      case TOK_WHILE:
4187
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
4188
 
        if (!js_EmitTree(cx, cg, pn->pn_left))
4189
 
            return JS_FALSE;
4190
 
        noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
4191
 
        if (noteIndex < 0)
4192
 
            return JS_FALSE;
4193
 
        beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
4194
 
        if (beq < 0)
4195
 
            return JS_FALSE;
4196
 
        if (!js_EmitTree(cx, cg, pn->pn_right))
4197
 
            return JS_FALSE;
4198
 
        jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
4199
 
        if (jmp < 0)
4200
 
            return JS_FALSE;
4201
 
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
4202
 
        if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
4203
 
            return JS_FALSE;
4204
 
        ok = js_PopStatementCG(cx, cg);
4205
 
        break;
4206
 
 
4207
 
      case TOK_DO:
4208
 
        /* Emit an annotated nop so we know to decompile a 'do' keyword. */
4209
 
        if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||
4210
 
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
4211
 
            return JS_FALSE;
4212
 
        }
4213
 
 
4214
 
        /* Compile the loop body. */
4215
 
        top = CG_OFFSET(cg);
4216
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top);
4217
 
        if (!js_EmitTree(cx, cg, pn->pn_left))
4218
 
            return JS_FALSE;
4219
 
 
4220
 
        /* Set loop and enclosing label update offsets, for continue. */
4221
 
        stmt = &stmtInfo;
4222
 
        do {
4223
 
            stmt->update = CG_OFFSET(cg);
4224
 
        } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
4225
 
 
4226
 
        /* Compile the loop condition, now that continues know where to go. */
4227
 
        if (!js_EmitTree(cx, cg, pn->pn_right))
4228
 
            return JS_FALSE;
4229
 
 
4230
 
        /*
4231
 
         * No source note needed, because JSOP_IFNE is used only for do-while.
4232
 
         * If we ever use JSOP_IFNE for other purposes, we can still avoid yet
4233
 
         * another note here, by storing (jmp - top) in the SRC_WHILE note's
4234
 
         * offset, and fetching that delta in order to decompile recursively.
4235
 
         */
4236
 
        if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0)
4237
 
            return JS_FALSE;
4238
 
        ok = js_PopStatementCG(cx, cg);
4239
 
        break;
4240
 
 
4241
 
      case TOK_FOR:
4242
 
        beq = 0;                /* suppress gcc warnings */
4243
 
        pn2 = pn->pn_left;
4244
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);
4245
 
 
4246
 
        if (pn2->pn_type == TOK_IN) {
4247
 
            JSBool emitIFEQ;
4248
 
 
4249
 
            /* Set stmtInfo type for later testing. */
4250
 
            stmtInfo.type = STMT_FOR_IN_LOOP;
4251
 
            noteIndex = -1;
4252
 
 
4253
 
            /*
4254
 
             * If the left part is 'var x', emit code to define x if necessary
4255
 
             * using a prolog opcode, but do not emit a pop.  If the left part
4256
 
             * is 'var x = i', emit prolog code to define x if necessary; then
4257
 
             * emit code to evaluate i, assign the result to x, and pop the
4258
 
             * result off the stack.
4259
 
             *
4260
 
             * All the logic to do this is implemented in the outer switch's
4261
 
             * TOK_VAR case, conditioned on pn_extra flags set by the parser.
4262
 
             *
4263
 
             * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3)
4264
 
             * called here will generate the proper note for the assignment
4265
 
             * op that sets x = i, hoisting the initialized var declaration
4266
 
             * out of the loop: 'var x = i; for (x in o) ...'.
4267
 
             *
4268
 
             * In the 'for (var x in o) ...' case, nothing but the prolog op
4269
 
             * (if needed) should be generated here, we must emit the note
4270
 
             * just before the JSOP_FOR* opcode in the switch on pn3->pn_type
4271
 
             * a bit below, so nothing is hoisted: 'for (var x in o) ...'.
4272
 
             *
4273
 
             * A 'for (let x = i in o)' loop must not be hoisted, since in
4274
 
             * this form the let variable is scoped by the loop body (but not
4275
 
             * the head).  The initializer expression i must be evaluated for
4276
 
             * any side effects.  So we hoist only i in the let case.
4277
 
             */
4278
 
            pn3 = pn2->pn_left;
4279
 
            type = pn3->pn_type;
4280
 
            cg->treeContext.flags |= TCF_IN_FOR_INIT;
4281
 
            if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3))
4282
 
                return JS_FALSE;
4283
 
            cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
4284
 
 
4285
 
            /* Emit a push to allocate the iterator. */
4286
 
            if (js_Emit1(cx, cg, JSOP_STARTITER) < 0)
4287
 
                return JS_FALSE;
4288
 
 
4289
 
            /* Compile the object expression to the right of 'in'. */
4290
 
            if (!js_EmitTree(cx, cg, pn2->pn_right))
4291
 
                return JS_FALSE;
4292
 
 
4293
 
            /*
4294
 
             * Emit a bytecode to convert top of stack value to the iterator
4295
 
             * object depending on the loop variant (for-in, for-each-in, or
4296
 
             * destructuring for-in).
4297
 
             */
4298
 
#if JS_HAS_DESTRUCTURING
4299
 
            JS_ASSERT(pn->pn_op == JSOP_FORIN ||
4300
 
                      pn->pn_op == JSOP_FOREACHKEYVAL ||
4301
 
                      pn->pn_op == JSOP_FOREACH);
4302
 
#else
4303
 
            JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH);
4304
 
#endif
4305
 
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
4306
 
                return JS_FALSE;
4307
 
 
4308
 
            top = CG_OFFSET(cg);
4309
 
            SET_STATEMENT_TOP(&stmtInfo, top);
4310
 
 
4311
 
            /*
4312
 
             * Compile a JSOP_FOR* bytecode based on the left hand side.
4313
 
             *
4314
 
             * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...|
4315
 
             * or similar, to signify assignment, rather than declaration, to
4316
 
             * the decompiler.  EmitDestructuringOps takes a prolog bytecode
4317
 
             * parameter and emits the appropriate source note, defaulting to
4318
 
             * assignment, so JSOP_SETNAME is not critical here; many similar
4319
 
             * ops could be used -- just not JSOP_NOP (which means 'let').
4320
 
             */
4321
 
            emitIFEQ = JS_TRUE;
4322
 
            op = JSOP_SETNAME;
4323
 
            switch (type) {
4324
 
#if JS_HAS_BLOCK_SCOPE
4325
 
              case TOK_LET:
4326
 
#endif
4327
 
              case TOK_VAR:
4328
 
                JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1);
4329
 
                pn3 = pn3->pn_head;
4330
 
#if JS_HAS_DESTRUCTURING
4331
 
                if (pn3->pn_type == TOK_ASSIGN) {
4332
 
                    pn3 = pn3->pn_left;
4333
 
                    JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC);
4334
 
                }
4335
 
                if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
4336
 
                    op = pn2->pn_left->pn_op;
4337
 
                    goto destructuring_for;
4338
 
                }
4339
 
#else
4340
 
                JS_ASSERT(pn3->pn_type == TOK_NAME);
4341
 
#endif
4342
 
                /*
4343
 
                 * Always annotate JSOP_FORLOCAL if given input of the form
4344
 
                 * 'for (let x in * o)' -- the decompiler must not hoist the
4345
 
                 * 'let x' out of the loop head, or x will be bound in the
4346
 
                 * wrong scope.  Likewise, but in this case only for the sake
4347
 
                 * of higher decompilation fidelity only, do not hoist 'var x'
4348
 
                 * when given 'for (var x in o)'.  But 'for (var x = i in o)'
4349
 
                 * requires hoisting in order to preserve the initializer i.
4350
 
                 * The decompiler can only handle so much!
4351
 
                 */
4352
 
                if ((
4353
 
#if JS_HAS_BLOCK_SCOPE
4354
 
                     type == TOK_LET ||
4355
 
#endif
4356
 
                     !pn3->pn_expr) &&
4357
 
                    js_NewSrcNote2(cx, cg, SRC_DECL,
4358
 
                                   type == TOK_VAR
4359
 
                                   ? SRC_DECL_VAR
4360
 
                                   : SRC_DECL_LET) < 0) {
4361
 
                    return JS_FALSE;
4362
 
                }
4363
 
                /* FALL THROUGH */
4364
 
              case TOK_NAME:
4365
 
                if (pn3->pn_slot >= 0) {
4366
 
                    op = pn3->pn_op;
4367
 
                    switch (op) {
4368
 
                      case JSOP_GETARG:   /* FALL THROUGH */
4369
 
                      case JSOP_SETARG:   op = JSOP_FORARG; break;
4370
 
                      case JSOP_GETVAR:   /* FALL THROUGH */
4371
 
                      case JSOP_SETVAR:   op = JSOP_FORVAR; break;
4372
 
                      case JSOP_GETGVAR:  /* FALL THROUGH */
4373
 
                      case JSOP_SETGVAR:  op = JSOP_FORNAME; break;
4374
 
                      case JSOP_GETLOCAL: /* FALL THROUGH */
4375
 
                      case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break;
4376
 
                      default:            JS_ASSERT(0);
4377
 
                    }
4378
 
                } else {
4379
 
                    pn3->pn_op = JSOP_FORNAME;
4380
 
                    if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE))
4381
 
                        return JS_FALSE;
4382
 
                    op = pn3->pn_op;
4383
 
                }
4384
 
                if (pn3->pn_slot >= 0) {
4385
 
                    if (pn3->pn_attrs & JSPROP_READONLY) {
4386
 
                        JS_ASSERT(op == JSOP_FORVAR);
4387
 
                        op = JSOP_GETVAR;
4388
 
                    }
4389
 
                    atomIndex = (jsatomid) pn3->pn_slot;
4390
 
                    EMIT_UINT16_IMM_OP(op, atomIndex);
4391
 
                } else {
4392
 
                    if (!EmitAtomOp(cx, pn3, op, cg))
4393
 
                        return JS_FALSE;
4394
 
                }
4395
 
                break;
4396
 
 
4397
 
              case TOK_DOT:
4398
 
                useful = JS_FALSE;
4399
 
                if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr,
4400
 
                                      &useful)) {
4401
 
                    return JS_FALSE;
4402
 
                }
4403
 
                if (!useful) {
4404
 
                    if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg))
4405
 
                        return JS_FALSE;
4406
 
                    break;
4407
 
                }
4408
 
                /* FALL THROUGH */
4409
 
 
4410
 
#if JS_HAS_DESTRUCTURING
4411
 
              case TOK_RB:
4412
 
              case TOK_RC:
4413
 
              destructuring_for:
4414
 
#endif
4415
 
#if JS_HAS_XML_SUPPORT
4416
 
              case TOK_UNARYOP:
4417
 
#endif
4418
 
#if JS_HAS_LVALUE_RETURN
4419
 
              case TOK_LP:
4420
 
#endif
4421
 
              case TOK_LB:
4422
 
                /*
4423
 
                 * We separate the first/next bytecode from the enumerator
4424
 
                 * variable binding to avoid any side-effects in the index
4425
 
                 * expression (e.g., for (x[i++] in {}) should not bind x[i]
4426
 
                 * or increment i at all).
4427
 
                 */
4428
 
                emitIFEQ = JS_FALSE;
4429
 
                if (!js_Emit1(cx, cg, JSOP_FORELEM))
4430
 
                    return JS_FALSE;
4431
 
 
4432
 
                /*
4433
 
                 * Emit a SRC_WHILE note with offset telling the distance to
4434
 
                 * the loop-closing jump (we can't reckon from the branch at
4435
 
                 * the top of the loop, because the loop-closing jump might
4436
 
                 * need to be an extended jump, independent of whether the
4437
 
                 * branch is short or long).
4438
 
                 */
4439
 
                noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
4440
 
                if (noteIndex < 0)
4441
 
                    return JS_FALSE;
4442
 
                beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
4443
 
                if (beq < 0)
4444
 
                    return JS_FALSE;
4445
 
 
4446
 
#if JS_HAS_DESTRUCTURING
4447
 
                if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
4448
 
                    if (!EmitDestructuringOps(cx, cg, op, pn3))
4449
 
                        return JS_FALSE;
4450
 
                    if (js_Emit1(cx, cg, JSOP_POP) < 0)
4451
 
                        return JS_FALSE;
4452
 
                    break;
4453
 
                }
4454
 
#endif
4455
 
#if JS_HAS_LVALUE_RETURN
4456
 
                if (pn3->pn_type == TOK_LP) {
4457
 
                    JS_ASSERT(pn3->pn_op == JSOP_SETCALL);
4458
 
                    if (!js_EmitTree(cx, cg, pn3))
4459
 
                        return JS_FALSE;
4460
 
                    if (!js_Emit1(cx, cg, JSOP_ENUMELEM))
4461
 
                        return JS_FALSE;
4462
 
                    break;
4463
 
                }
4464
 
#endif
4465
 
#if JS_HAS_XML_SUPPORT
4466
 
                if (pn3->pn_type == TOK_UNARYOP) {
4467
 
                    JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME);
4468
 
                    if (!js_EmitTree(cx, cg, pn3))
4469
 
                        return JS_FALSE;
4470
 
                    if (!js_Emit1(cx, cg, JSOP_ENUMELEM))
4471
 
                        return JS_FALSE;
4472
 
                    break;
4473
 
                }
4474
 
#endif
4475
 
 
4476
 
                /* Now that we're safely past the IFEQ, commit side effects. */
4477
 
                if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))
4478
 
                    return JS_FALSE;
4479
 
                break;
4480
 
 
4481
 
              default:
4482
 
                JS_ASSERT(0);
4483
 
            }
4484
 
 
4485
 
            if (emitIFEQ) {
4486
 
                /* Annotate so the decompiler can find the loop-closing jump. */
4487
 
                noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
4488
 
                if (noteIndex < 0)
4489
 
                    return JS_FALSE;
4490
 
 
4491
 
                /* Pop and test the loop condition generated by JSOP_FOR*. */
4492
 
                beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
4493
 
                if (beq < 0)
4494
 
                    return JS_FALSE;
4495
 
            }
4496
 
        } else {
4497
 
            op = JSOP_POP;
4498
 
            if (!pn2->pn_kid1) {
4499
 
                /* No initializer: emit an annotated nop for the decompiler. */
4500
 
                op = JSOP_NOP;
4501
 
            } else {
4502
 
                cg->treeContext.flags |= TCF_IN_FOR_INIT;
4503
 
#if JS_HAS_DESTRUCTURING
4504
 
                pn3 = pn2->pn_kid1;
4505
 
                if (pn3->pn_type == TOK_ASSIGN &&
4506
 
                    !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
4507
 
                    return JS_FALSE;
4508
 
                }
4509
 
#endif
4510
 
                if (op == JSOP_POP) {
4511
 
                    if (!js_EmitTree(cx, cg, pn3))
4512
 
                        return JS_FALSE;
4513
 
                    if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) {
4514
 
                        /*
4515
 
                         * Check whether a destructuring-initialized var decl
4516
 
                         * was optimized to a group assignment.  If so, we do
4517
 
                         * not need to emit a pop below, so switch to a nop,
4518
 
                         * just for the decompiler.
4519
 
                         */
4520
 
                        JS_ASSERT(pn3->pn_arity == PN_LIST);
4521
 
                        if (pn3->pn_extra & PNX_GROUPINIT)
4522
 
                            op = JSOP_NOP;
4523
 
                    }
4524
 
                }
4525
 
                cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
4526
 
            }
4527
 
            noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
4528
 
            if (noteIndex < 0 ||
4529
 
                js_Emit1(cx, cg, op) < 0) {
4530
 
                return JS_FALSE;
4531
 
            }
4532
 
 
4533
 
            top = CG_OFFSET(cg);
4534
 
            SET_STATEMENT_TOP(&stmtInfo, top);
4535
 
            if (!pn2->pn_kid2) {
4536
 
                /* No loop condition: flag this fact in the source notes. */
4537
 
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0))
4538
 
                    return JS_FALSE;
4539
 
            } else {
4540
 
                if (!js_EmitTree(cx, cg, pn2->pn_kid2))
4541
 
                    return JS_FALSE;
4542
 
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
4543
 
                                         CG_OFFSET(cg) - top)) {
4544
 
                    return JS_FALSE;
4545
 
                }
4546
 
                beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
4547
 
                if (beq < 0)
4548
 
                    return JS_FALSE;
4549
 
            }
4550
 
 
4551
 
            /* Set pn3 (used below) here to avoid spurious gcc warnings. */
4552
 
            pn3 = pn2->pn_kid3;
4553
 
        }
4554
 
 
4555
 
        /* Emit code for the loop body. */
4556
 
        if (!js_EmitTree(cx, cg, pn->pn_right))
4557
 
            return JS_FALSE;
4558
 
 
4559
 
        if (pn2->pn_type != TOK_IN) {
4560
 
            /* Set the second note offset so we can find the update part. */
4561
 
            JS_ASSERT(noteIndex != -1);
4562
 
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
4563
 
                                     CG_OFFSET(cg) - top)) {
4564
 
                return JS_FALSE;
4565
 
            }
4566
 
 
4567
 
            if (pn3) {
4568
 
                /* Set loop and enclosing "update" offsets, for continue. */
4569
 
                stmt = &stmtInfo;
4570
 
                do {
4571
 
                    stmt->update = CG_OFFSET(cg);
4572
 
                } while ((stmt = stmt->down) != NULL &&
4573
 
                         stmt->type == STMT_LABEL);
4574
 
 
4575
 
                op = JSOP_POP;
4576
 
#if JS_HAS_DESTRUCTURING
4577
 
                if (pn3->pn_type == TOK_ASSIGN &&
4578
 
                    !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
4579
 
                    return JS_FALSE;
4580
 
                }
4581
 
#endif
4582
 
                if (op == JSOP_POP) {
4583
 
                    if (!js_EmitTree(cx, cg, pn3))
4584
 
                        return JS_FALSE;
4585
 
                    if (js_Emit1(cx, cg, op) < 0)
4586
 
                        return JS_FALSE;
4587
 
                }
4588
 
 
4589
 
                /* Restore the absolute line number for source note readers. */
4590
 
                off = (ptrdiff_t) pn->pn_pos.end.lineno;
4591
 
                if (CG_CURRENT_LINE(cg) != (uintN) off) {
4592
 
                    if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
4593
 
                        return JS_FALSE;
4594
 
                    CG_CURRENT_LINE(cg) = (uintN) off;
4595
 
                }
4596
 
            }
4597
 
 
4598
 
            /* The third note offset helps us find the loop-closing jump. */
4599
 
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2,
4600
 
                                     CG_OFFSET(cg) - top)) {
4601
 
                return JS_FALSE;
4602
 
            }
4603
 
        }
4604
 
 
4605
 
        /* Emit the loop-closing jump and fixup all jump offsets. */
4606
 
        jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
4607
 
        if (jmp < 0)
4608
 
            return JS_FALSE;
4609
 
        if (beq > 0)
4610
 
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
4611
 
        if (pn2->pn_type == TOK_IN) {
4612
 
            /* Set the SRC_WHILE note offset so we can find the closing jump. */
4613
 
            JS_ASSERT(noteIndex != -1);
4614
 
            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq))
4615
 
                return JS_FALSE;
4616
 
        }
4617
 
 
4618
 
        /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */
4619
 
        if (!js_PopStatementCG(cx, cg))
4620
 
            return JS_FALSE;
4621
 
 
4622
 
        if (pn2->pn_type == TOK_IN) {
4623
 
            if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
4624
 
                return JS_FALSE;
4625
 
        }
4626
 
        break;
4627
 
 
4628
 
      case TOK_BREAK:
4629
 
        stmt = cg->treeContext.topStmt;
4630
 
        atom = pn->pn_atom;
4631
 
        if (atom) {
4632
 
            ale = js_IndexAtom(cx, atom, &cg->atomList);
4633
 
            if (!ale)
4634
 
                return JS_FALSE;
4635
 
            while (stmt->type != STMT_LABEL || stmt->atom != atom)
4636
 
                stmt = stmt->down;
4637
 
            noteType = SRC_BREAK2LABEL;
4638
 
        } else {
4639
 
            ale = NULL;
4640
 
            while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
4641
 
                stmt = stmt->down;
4642
 
            noteType = SRC_NULL;
4643
 
        }
4644
 
 
4645
 
        if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0)
4646
 
            return JS_FALSE;
4647
 
        break;
4648
 
 
4649
 
      case TOK_CONTINUE:
4650
 
        stmt = cg->treeContext.topStmt;
4651
 
        atom = pn->pn_atom;
4652
 
        if (atom) {
4653
 
            /* Find the loop statement enclosed by the matching label. */
4654
 
            JSStmtInfo *loop = NULL;
4655
 
            ale = js_IndexAtom(cx, atom, &cg->atomList);
4656
 
            if (!ale)
4657
 
                return JS_FALSE;
4658
 
            while (stmt->type != STMT_LABEL || stmt->atom != atom) {
4659
 
                if (STMT_IS_LOOP(stmt))
4660
 
                    loop = stmt;
4661
 
                stmt = stmt->down;
4662
 
            }
4663
 
            stmt = loop;
4664
 
            noteType = SRC_CONT2LABEL;
4665
 
        } else {
4666
 
            ale = NULL;
4667
 
            while (!STMT_IS_LOOP(stmt))
4668
 
                stmt = stmt->down;
4669
 
            noteType = SRC_CONTINUE;
4670
 
        }
4671
 
 
4672
 
        if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0)
4673
 
            return JS_FALSE;
4674
 
        break;
4675
 
 
4676
 
      case TOK_WITH:
4677
 
        if (!js_EmitTree(cx, cg, pn->pn_left))
4678
 
            return JS_FALSE;
4679
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg));
4680
 
        if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
4681
 
            return JS_FALSE;
4682
 
        if (!js_EmitTree(cx, cg, pn->pn_right))
4683
 
            return JS_FALSE;
4684
 
        if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
4685
 
            return JS_FALSE;
4686
 
        ok = js_PopStatementCG(cx, cg);
4687
 
        break;
4688
 
 
4689
 
      case TOK_TRY:
4690
 
      {
4691
 
        ptrdiff_t start, end, catchJump, catchStart, finallyCatch;
4692
 
        intN depth;
4693
 
        JSParseNode *lastCatch;
4694
 
 
4695
 
        catchJump = catchStart = finallyCatch = -1;
4696
 
 
4697
 
        /*
4698
 
         * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
4699
 
         * for later fixup.
4700
 
         *
4701
 
         * When a finally block is 'active' (STMT_FINALLY on the treeContext),
4702
 
         * non-local jumps (including jumps-over-catches) result in a GOSUB
4703
 
         * being written into the bytecode stream and fixed-up later (c.f.
4704
 
         * EmitBackPatchOp and BackPatch).
4705
 
         */
4706
 
        js_PushStatement(&cg->treeContext, &stmtInfo,
4707
 
                         pn->pn_kid3 ? STMT_FINALLY : STMT_TRY,
4708
 
                         CG_OFFSET(cg));
4709
 
 
4710
 
        /*
4711
 
         * About JSOP_SETSP: an exception can be thrown while the stack is in
4712
 
         * an unbalanced state, and this imbalance causes problems with things
4713
 
         * like function invocation later on.
4714
 
         *
4715
 
         * To fix this, we compute the 'balanced' stack depth upon try entry,
4716
 
         * and then restore the stack to this depth when we hit the first catch
4717
 
         * or finally block.  We can't just zero the stack, because things like
4718
 
         * for/in and with that are active upon entry to the block keep state
4719
 
         * variables on the stack.
4720
 
         */
4721
 
        depth = cg->stackDepth;
4722
 
 
4723
 
        /* Mark try location for decompilation, then emit try block. */
4724
 
        if (js_Emit1(cx, cg, JSOP_TRY) < 0)
4725
 
            return JS_FALSE;
4726
 
        start = CG_OFFSET(cg);
4727
 
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
4728
 
            return JS_FALSE;
4729
 
 
4730
 
        /* GOSUB to finally, if present. */
4731
 
        if (pn->pn_kid3) {
4732
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
4733
 
                return JS_FALSE;
4734
 
            jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo));
4735
 
            if (jmp < 0)
4736
 
                return JS_FALSE;
4737
 
 
4738
 
            /* JSOP_RETSUB pops the return pc-index, balancing the stack. */
4739
 
            cg->stackDepth = depth;
4740
 
        }
4741
 
 
4742
 
        /* Emit (hidden) jump over catch and/or finally. */
4743
 
        if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
4744
 
            return JS_FALSE;
4745
 
        jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
4746
 
        if (jmp < 0)
4747
 
            return JS_FALSE;
4748
 
 
4749
 
        end = CG_OFFSET(cg);
4750
 
 
4751
 
        /* If this try has a catch block, emit it. */
4752
 
        pn2 = pn->pn_kid2;
4753
 
        lastCatch = NULL;
4754
 
        if (pn2) {
4755
 
            jsint count = 0;    /* previous catch block's population */
4756
 
 
4757
 
            catchStart = end;
4758
 
 
4759
 
            /*
4760
 
             * The emitted code for a catch block looks like:
4761
 
             *
4762
 
             * [throwing]                          only if 2nd+ catch block
4763
 
             * [leaveblock]                        only if 2nd+ catch block
4764
 
             * enterblock                          with SRC_CATCH
4765
 
             * exception
4766
 
             * [dup]                               only if catchguard
4767
 
             * setlocalpop <slot>                  or destructuring code
4768
 
             * [< catchguard code >]               if there's a catchguard
4769
 
             * [ifeq <offset to next catch block>]         " "
4770
 
             * [pop]                               only if catchguard
4771
 
             * < catch block contents >
4772
 
             * leaveblock
4773
 
             * goto <end of catch blocks>          non-local; finally applies
4774
 
             *
4775
 
             * If there's no catch block without a catchguard, the last
4776
 
             * <offset to next catch block> points to rethrow code.  This
4777
 
             * code will [gosub] to the finally code if appropriate, and is
4778
 
             * also used for the catch-all trynote for capturing exceptions
4779
 
             * thrown from catch{} blocks.
4780
 
             */
4781
 
            for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
4782
 
                ptrdiff_t guardJump, catchNote;
4783
 
 
4784
 
                guardJump = GUARDJUMP(stmtInfo);
4785
 
                if (guardJump == -1) {
4786
 
                    /* Set stack to original depth (see SETSP comment above). */
4787
 
                    EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
4788
 
                    cg->stackDepth = depth;
4789
 
                } else {
4790
 
                    /* Fix up and clean up previous catch block. */
4791
 
                    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
4792
 
 
4793
 
                    /*
4794
 
                     * Account for the pushed exception object that we still
4795
 
                     * have after the jumping from the previous guard.
4796
 
                     */
4797
 
                    JS_ASSERT(cg->stackDepth == depth);
4798
 
                    cg->stackDepth = depth + 1;
4799
 
 
4800
 
                    /*
4801
 
                     * Move exception back to cx->exception to prepare for
4802
 
                     * the next catch. We hide [throwing] from the decompiler
4803
 
                     * since it compensates for the hidden JSOP_DUP at the
4804
 
                     * start of the previous guarded catch.
4805
 
                     */
4806
 
                    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
4807
 
                        js_Emit1(cx, cg, JSOP_THROWING) < 0) {
4808
 
                        return JS_FALSE;
4809
 
                    }
4810
 
 
4811
 
                    /*
4812
 
                     * Emit an unbalanced [leaveblock] for the previous catch,
4813
 
                     * whose block object count is saved below.
4814
 
                     */
4815
 
                    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
4816
 
                        return JS_FALSE;
4817
 
                    JS_ASSERT(count >= 0);
4818
 
                    EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
4819
 
                }
4820
 
 
4821
 
                /*
4822
 
                 * Annotate the JSOP_ENTERBLOCK that's about to be generated
4823
 
                 * by the call to js_EmitTree immediately below.  Save this
4824
 
                 * source note's index in stmtInfo for use by the TOK_CATCH:
4825
 
                 * case, where the length of the catch guard is set as the
4826
 
                 * note's offset.
4827
 
                 */
4828
 
                catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
4829
 
                if (catchNote < 0)
4830
 
                    return JS_FALSE;
4831
 
                CATCHNOTE(stmtInfo) = catchNote;
4832
 
 
4833
 
                /*
4834
 
                 * Emit the lexical scope and catch body.  Save the catch's
4835
 
                 * block object population via count, for use when targeting
4836
 
                 * guardJump at the next catch (the guard mismatch case).
4837
 
                 */
4838
 
                JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE);
4839
 
                count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom));
4840
 
                if (!js_EmitTree(cx, cg, pn3))
4841
 
                    return JS_FALSE;
4842
 
 
4843
 
                /* gosub <finally>, if required */
4844
 
                if (pn->pn_kid3) {
4845
 
                    jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
4846
 
                                          &GOSUBS(stmtInfo));
4847
 
                    if (jmp < 0)
4848
 
                        return JS_FALSE;
4849
 
                    JS_ASSERT(cg->stackDepth == depth);
4850
 
                }
4851
 
 
4852
 
                /*
4853
 
                 * Jump over the remaining catch blocks.  This will get fixed
4854
 
                 * up to jump to after catch/finally.
4855
 
                 */
4856
 
                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
4857
 
                    return JS_FALSE;
4858
 
                jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
4859
 
                if (jmp < 0)
4860
 
                    return JS_FALSE;
4861
 
 
4862
 
                /*
4863
 
                 * Save a pointer to the last catch node to handle try-finally
4864
 
                 * and try-catch(guard)-finally special cases.
4865
 
                 */
4866
 
                lastCatch = pn3->pn_expr;
4867
 
            }
4868
 
        }
4869
 
 
4870
 
        /*
4871
 
         * Last catch guard jumps to the rethrow code sequence if none of the
4872
 
         * guards match. Target guardJump at the beginning of the rethrow
4873
 
         * sequence, just in case a guard expression throws and leaves the
4874
 
         * stack unbalanced.
4875
 
         */
4876
 
        if (lastCatch && lastCatch->pn_kid2) {
4877
 
            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo));
4878
 
 
4879
 
            /* Sync the stack to take into account pushed exception. */
4880
 
            JS_ASSERT(cg->stackDepth == depth);
4881
 
            cg->stackDepth = depth + 1;
4882
 
 
4883
 
            /*
4884
 
             * Rethrow the exception, delegating executing of finally if any
4885
 
             * to the exception handler.
4886
 
             */
4887
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
4888
 
                js_Emit1(cx, cg, JSOP_THROW) < 0) {
4889
 
                return JS_FALSE;
4890
 
            }
4891
 
        }
4892
 
 
4893
 
        JS_ASSERT(cg->stackDepth == depth);
4894
 
 
4895
 
        /* Emit finally handler if any. */
4896
 
        if (pn->pn_kid3) {
4897
 
            /*
4898
 
             * We emit [setsp][gosub] to call try-finally when an exception is
4899
 
             * thrown from try or try-catch blocks. The [gosub] and [retsub]
4900
 
             * opcodes will take care of stacking and rethrowing any exception
4901
 
             * pending across the finally.
4902
 
             */
4903
 
            finallyCatch = CG_OFFSET(cg);
4904
 
            EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
4905
 
 
4906
 
            jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
4907
 
                                  &GOSUBS(stmtInfo));
4908
 
            if (jmp < 0)
4909
 
                return JS_FALSE;
4910
 
 
4911
 
            JS_ASSERT(cg->stackDepth == depth);
4912
 
            JS_ASSERT((uintN)depth <= cg->maxStackDepth);
4913
 
 
4914
 
            /*
4915
 
             * Fix up the gosubs that might have been emitted before non-local
4916
 
             * jumps to the finally code.
4917
 
             */
4918
 
            if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB))
4919
 
                return JS_FALSE;
4920
 
 
4921
 
            /*
4922
 
             * The stack budget must be balanced at this point.  All [gosub]
4923
 
             * calls emitted before this point will push two stack slots, one
4924
 
             * for the pending exception (or JSVAL_HOLE if there is no pending
4925
 
             * exception) and one for the [retsub] pc-index.
4926
 
             */
4927
 
            JS_ASSERT(cg->stackDepth == depth);
4928
 
            cg->stackDepth += 2;
4929
 
            if ((uintN)cg->stackDepth > cg->maxStackDepth)
4930
 
                cg->maxStackDepth = cg->stackDepth;
4931
 
 
4932
 
            /* Now indicate that we're emitting a subroutine body. */
4933
 
            stmtInfo.type = STMT_SUBROUTINE;
4934
 
            if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3))
4935
 
                return JS_FALSE;
4936
 
            if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||
4937
 
                !js_EmitTree(cx, cg, pn->pn_kid3) ||
4938
 
                js_Emit1(cx, cg, JSOP_RETSUB) < 0) {
4939
 
                return JS_FALSE;
4940
 
            }
4941
 
 
4942
 
            /* Restore stack depth budget to its balanced state. */
4943
 
            JS_ASSERT(cg->stackDepth == depth + 2);
4944
 
            cg->stackDepth = depth;
4945
 
        }
4946
 
        if (!js_PopStatementCG(cx, cg))
4947
 
            return JS_FALSE;
4948
 
 
4949
 
        if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
4950
 
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
4951
 
            return JS_FALSE;
4952
 
        }
4953
 
 
4954
 
        /* Fix up the end-of-try/catch jumps to come here. */
4955
 
        if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO))
4956
 
            return JS_FALSE;
4957
 
 
4958
 
        /*
4959
 
         * Add the try note last, to let post-order give us the right ordering
4960
 
         * (first to last for a given nesting level, inner to outer by level).
4961
 
         */
4962
 
        if (pn->pn_kid2) {
4963
 
            JS_ASSERT(end != -1 && catchStart != -1);
4964
 
            if (!js_NewTryNote(cx, cg, start, end, catchStart))
4965
 
                return JS_FALSE;
4966
 
        }
4967
 
 
4968
 
        /*
4969
 
         * If we've got a finally, mark try+catch region with additional
4970
 
         * trynote to catch exceptions (re)thrown from a catch block or
4971
 
         * for the try{}finally{} case.
4972
 
         */
4973
 
        if (pn->pn_kid3) {
4974
 
            JS_ASSERT(finallyCatch != -1);
4975
 
            if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch))
4976
 
                return JS_FALSE;
4977
 
        }
4978
 
        break;
4979
 
      }
4980
 
 
4981
 
      case TOK_CATCH:
4982
 
      {
4983
 
        ptrdiff_t catchStart, guardJump;
4984
 
 
4985
 
        /*
4986
 
         * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
4987
 
         * and save the block object atom.
4988
 
         */
4989
 
        stmt = cg->treeContext.topStmt;
4990
 
        JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE));
4991
 
        stmt->type = STMT_CATCH;
4992
 
        catchStart = stmt->update;
4993
 
        atom = stmt->atom;
4994
 
 
4995
 
        /* Go up one statement info record to the TRY or FINALLY record. */
4996
 
        stmt = stmt->down;
4997
 
        JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);
4998
 
 
4999
 
        /* Pick up the pending exception and bind it to the catch variable. */
5000
 
        if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
5001
 
            return JS_FALSE;
5002
 
 
5003
 
        /*
5004
 
         * Dup the exception object if there is a guard for rethrowing to use
5005
 
         * it later when rethrowing or in other catches.
5006
 
         */
5007
 
        if (pn->pn_kid2) {
5008
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
5009
 
                js_Emit1(cx, cg, JSOP_DUP) < 0) {
5010
 
                return JS_FALSE;
5011
 
            }
5012
 
        }
5013
 
 
5014
 
        pn2 = pn->pn_kid1;
5015
 
        switch (pn2->pn_type) {
5016
 
#if JS_HAS_DESTRUCTURING
5017
 
          case TOK_RB:
5018
 
          case TOK_RC:
5019
 
            if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2))
5020
 
                return JS_FALSE;
5021
 
            if (js_Emit1(cx, cg, JSOP_POP) < 0)
5022
 
                return JS_FALSE;
5023
 
            break;
5024
 
#endif
5025
 
 
5026
 
          case TOK_NAME:
5027
 
            /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */
5028
 
            pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom));
5029
 
            EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot);
5030
 
            break;
5031
 
 
5032
 
          default:
5033
 
            JS_ASSERT(0);
5034
 
        }
5035
 
 
5036
 
        /* Emit the guard expression, if there is one. */
5037
 
        if (pn->pn_kid2) {
5038
 
            if (!js_EmitTree(cx, cg, pn->pn_kid2))
5039
 
                return JS_FALSE;
5040
 
            if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0,
5041
 
                                     CG_OFFSET(cg) - catchStart)) {
5042
 
                return JS_FALSE;
5043
 
            }
5044
 
            /* ifeq <next block> */
5045
 
            guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
5046
 
            if (guardJump < 0)
5047
 
                return JS_FALSE;
5048
 
            GUARDJUMP(*stmt) = guardJump;
5049
 
 
5050
 
            /* Pop duplicated exception object as we no longer need it. */
5051
 
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
5052
 
                js_Emit1(cx, cg, JSOP_POP) < 0) {
5053
 
                return JS_FALSE;
5054
 
            }
5055
 
        }
5056
 
 
5057
 
        /* Emit the catch body. */
5058
 
        if (!js_EmitTree(cx, cg, pn->pn_kid3))
5059
 
            return JS_FALSE;
5060
 
 
5061
 
        /*
5062
 
         * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via
5063
 
         * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop.
5064
 
         */
5065
 
        off = cg->stackDepth;
5066
 
        if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0)
5067
 
            return JS_FALSE;
5068
 
        break;
5069
 
      }
5070
 
 
5071
 
      case TOK_VAR:
5072
 
        if (!EmitVariables(cx, cg, pn, JS_FALSE, &noteIndex))
5073
 
            return JS_FALSE;
5074
 
        break;
5075
 
 
5076
 
      case TOK_RETURN:
5077
 
        /* Push a return value */
5078
 
        pn2 = pn->pn_kid;
5079
 
        if (pn2) {
5080
 
            if (!js_EmitTree(cx, cg, pn2))
5081
 
                return JS_FALSE;
5082
 
        } else {
5083
 
            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
5084
 
                return JS_FALSE;
5085
 
        }
5086
 
 
5087
 
        /*
5088
 
         * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
5089
 
         * JSOP_SETRVAL if there are open try blocks having finally clauses.
5090
 
         * We can't simply transfer control flow to our caller in that case,
5091
 
         * because we must gosub to those clauses from inner to outer, with
5092
 
         * the correct stack pointer (i.e., after popping any with, for/in,
5093
 
         * etc., slots nested inside the finally's try).
5094
 
         */
5095
 
        op = JSOP_RETURN;
5096
 
        if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
5097
 
            return JS_FALSE;
5098
 
        if (js_Emit1(cx, cg, op) < 0)
5099
 
            return JS_FALSE;
5100
 
        break;
5101
 
 
5102
 
#if JS_HAS_GENERATORS
5103
 
      case TOK_YIELD:
5104
 
        if (pn->pn_kid) {
5105
 
            if (!js_EmitTree(cx, cg, pn->pn_kid))
5106
 
                return JS_FALSE;
5107
 
        } else {
5108
 
            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
5109
 
                return JS_FALSE;
5110
 
        }
5111
 
        if (js_Emit1(cx, cg, JSOP_YIELD) < 0)
5112
 
            return JS_FALSE;
5113
 
        break;
5114
 
#endif
5115
 
 
5116
 
      case TOK_LC:
5117
 
#if JS_HAS_XML_SUPPORT
5118
 
        if (pn->pn_arity == PN_UNARY) {
5119
 
            if (!js_EmitTree(cx, cg, pn->pn_kid))
5120
 
                return JS_FALSE;
5121
 
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
5122
 
                return JS_FALSE;
5123
 
            break;
5124
 
        }
5125
 
#endif
5126
 
 
5127
 
        JS_ASSERT(pn->pn_arity == PN_LIST);
5128
 
 
5129
 
        noteIndex = -1;
5130
 
        tmp = CG_OFFSET(cg);
5131
 
        if (pn->pn_extra & PNX_NEEDBRACES) {
5132
 
            noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
5133
 
            if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0)
5134
 
                return JS_FALSE;
5135
 
        }
5136
 
 
5137
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
5138
 
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5139
 
            if (!js_EmitTree(cx, cg, pn2))
5140
 
                return JS_FALSE;
5141
 
        }
5142
 
 
5143
 
        if (noteIndex >= 0 &&
5144
 
            !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
5145
 
                                 CG_OFFSET(cg) - tmp)) {
5146
 
            return JS_FALSE;
5147
 
        }
5148
 
 
5149
 
        ok = js_PopStatementCG(cx, cg);
5150
 
        break;
5151
 
 
5152
 
      case TOK_BODY:
5153
 
        JS_ASSERT(pn->pn_arity == PN_LIST);
5154
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top);
5155
 
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5156
 
            if (!js_EmitTree(cx, cg, pn2))
5157
 
                return JS_FALSE;
5158
 
        }
5159
 
        ok = js_PopStatementCG(cx, cg);
5160
 
        break;
5161
 
 
5162
 
      case TOK_SEMI:
5163
 
        pn2 = pn->pn_kid;
5164
 
        if (pn2) {
5165
 
            /*
5166
 
             * Top-level or called-from-a-native JS_Execute/EvaluateScript,
5167
 
             * debugger, and eval frames may need the value of the ultimate
5168
 
             * expression statement as the script's result, despite the fact
5169
 
             * that it appears useless to the compiler.
5170
 
             */
5171
 
            useful = wantval = !cx->fp->fun ||
5172
 
                               !FUN_INTERPRETED(cx->fp->fun) ||
5173
 
                               (cx->fp->flags & JSFRAME_SPECIAL);
5174
 
            if (!useful) {
5175
 
                if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
5176
 
                    return JS_FALSE;
5177
 
            }
5178
 
 
5179
 
            /*
5180
 
             * Don't eliminate apparently useless expressions if they are
5181
 
             * labeled expression statements.  The tc->topStmt->update test
5182
 
             * catches the case where we are nesting in js_EmitTree for a
5183
 
             * labeled compound statement.
5184
 
             */
5185
 
            if (!useful &&
5186
 
                (!cg->treeContext.topStmt ||
5187
 
                 cg->treeContext.topStmt->type != STMT_LABEL ||
5188
 
                 cg->treeContext.topStmt->update < CG_OFFSET(cg))) {
5189
 
                CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno;
5190
 
                if (!js_ReportCompileErrorNumber(cx, cg,
5191
 
                                                 JSREPORT_CG |
5192
 
                                                 JSREPORT_WARNING |
5193
 
                                                 JSREPORT_STRICT,
5194
 
                                                 JSMSG_USELESS_EXPR)) {
5195
 
                    return JS_FALSE;
5196
 
                }
5197
 
            } else {
5198
 
                op = wantval ? JSOP_POPV : JSOP_POP;
5199
 
#if JS_HAS_DESTRUCTURING
5200
 
                if (!wantval &&
5201
 
                    pn2->pn_type == TOK_ASSIGN &&
5202
 
                    !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) {
5203
 
                    return JS_FALSE;
5204
 
                }
5205
 
#endif
5206
 
                if (op != JSOP_NOP) {
5207
 
                    if (!js_EmitTree(cx, cg, pn2))
5208
 
                        return JS_FALSE;
5209
 
                    if (js_Emit1(cx, cg, op) < 0)
5210
 
                        return JS_FALSE;
5211
 
                }
5212
 
            }
5213
 
        }
5214
 
        break;
5215
 
 
5216
 
      case TOK_COLON:
5217
 
        /* Emit an annotated nop so we know to decompile a label. */
5218
 
        atom = pn->pn_atom;
5219
 
        ale = js_IndexAtom(cx, atom, &cg->atomList);
5220
 
        if (!ale)
5221
 
            return JS_FALSE;
5222
 
        pn2 = pn->pn_expr;
5223
 
        noteType = (pn2->pn_type == TOK_LC ||
5224
 
                    (pn2->pn_type == TOK_LEXICALSCOPE &&
5225
 
                     pn2->pn_expr->pn_type == TOK_LC))
5226
 
                   ? SRC_LABELBRACE
5227
 
                   : SRC_LABEL;
5228
 
        noteIndex = js_NewSrcNote2(cx, cg, noteType,
5229
 
                                   (ptrdiff_t) ALE_INDEX(ale));
5230
 
        if (noteIndex < 0 ||
5231
 
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
5232
 
            return JS_FALSE;
5233
 
        }
5234
 
 
5235
 
        /* Emit code for the labeled statement. */
5236
 
        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL,
5237
 
                         CG_OFFSET(cg));
5238
 
        stmtInfo.atom = atom;
5239
 
        if (!js_EmitTree(cx, cg, pn2))
5240
 
            return JS_FALSE;
5241
 
        if (!js_PopStatementCG(cx, cg))
5242
 
            return JS_FALSE;
5243
 
 
5244
 
        /* If the statement was compound, emit a note for the end brace. */
5245
 
        if (noteType == SRC_LABELBRACE) {
5246
 
            if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
5247
 
                js_Emit1(cx, cg, JSOP_NOP) < 0) {
5248
 
                return JS_FALSE;
5249
 
            }
5250
 
        }
5251
 
        break;
5252
 
 
5253
 
      case TOK_COMMA:
5254
 
        /*
5255
 
         * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
5256
 
         * These notes help the decompiler bracket the bytecodes generated
5257
 
         * from each sub-expression that follows a comma.
5258
 
         */
5259
 
        off = noteIndex = -1;
5260
 
        for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
5261
 
            if (!js_EmitTree(cx, cg, pn2))
5262
 
                return JS_FALSE;
5263
 
            tmp = CG_OFFSET(cg);
5264
 
            if (noteIndex >= 0) {
5265
 
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
5266
 
                    return JS_FALSE;
5267
 
            }
5268
 
            if (!pn2->pn_next)
5269
 
                break;
5270
 
            off = tmp;
5271
 
            noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
5272
 
            if (noteIndex < 0 ||
5273
 
                js_Emit1(cx, cg, JSOP_POP) < 0) {
5274
 
                return JS_FALSE;
5275
 
            }
5276
 
        }
5277
 
        break;
5278
 
 
5279
 
      case TOK_ASSIGN:
5280
 
        /*
5281
 
         * Check left operand type and generate specialized code for it.
5282
 
         * Specialize to avoid ECMA "reference type" values on the operand
5283
 
         * stack, which impose pervasive runtime "GetValue" costs.
5284
 
         */
5285
 
        pn2 = pn->pn_left;
5286
 
        JS_ASSERT(pn2->pn_type != TOK_RP);
5287
 
        atomIndex = (jsatomid) -1;
5288
 
        switch (pn2->pn_type) {
5289
 
          case TOK_NAME:
5290
 
            if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
5291
 
                return JS_FALSE;
5292
 
            if (pn2->pn_slot >= 0) {
5293
 
                atomIndex = (jsatomid) pn2->pn_slot;
5294
 
            } else {
5295
 
                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
5296
 
                if (!ale)
5297
 
                    return JS_FALSE;
5298
 
                atomIndex = ALE_INDEX(ale);
5299
 
                EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
5300
 
            }
5301
 
            break;
5302
 
          case TOK_DOT:
5303
 
            if (!js_EmitTree(cx, cg, pn2->pn_expr))
5304
 
                return JS_FALSE;
5305
 
            ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
5306
 
            if (!ale)
5307
 
                return JS_FALSE;
5308
 
            atomIndex = ALE_INDEX(ale);
5309
 
            break;
5310
 
          case TOK_LB:
5311
 
            JS_ASSERT(pn2->pn_arity == PN_BINARY);
5312
 
            if (!js_EmitTree(cx, cg, pn2->pn_left))
5313
 
                return JS_FALSE;
5314
 
            if (!js_EmitTree(cx, cg, pn2->pn_right))
5315
 
                return JS_FALSE;
5316
 
            break;
5317
 
#if JS_HAS_DESTRUCTURING
5318
 
          case TOK_RB:
5319
 
          case TOK_RC:
5320
 
            break;
5321
 
#endif
5322
 
#if JS_HAS_LVALUE_RETURN
5323
 
          case TOK_LP:
5324
 
            if (!js_EmitTree(cx, cg, pn2))
5325
 
                return JS_FALSE;
5326
 
            break;
5327
 
#endif
5328
 
#if JS_HAS_XML_SUPPORT
5329
 
          case TOK_UNARYOP:
5330
 
            JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
5331
 
            if (!js_EmitTree(cx, cg, pn2->pn_kid))
5332
 
                return JS_FALSE;
5333
 
            if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0)
5334
 
                return JS_FALSE;
5335
 
            break;
5336
 
#endif
5337
 
          default:
5338
 
            JS_ASSERT(0);
5339
 
        }
5340
 
 
5341
 
        op = pn->pn_op;
5342
 
#if JS_HAS_GETTER_SETTER
5343
 
        if (op == JSOP_GETTER || op == JSOP_SETTER) {
5344
 
            /* We'll emit these prefix bytecodes after emitting the r.h.s. */
5345
 
            if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) {
5346
 
                ReportStatementTooLarge(cx, cg);
5347
 
                return JS_FALSE;
5348
 
            }
5349
 
        } else
5350
 
#endif
5351
 
        /* If += or similar, dup the left operand and get its value. */
5352
 
        if (op != JSOP_NOP) {
5353
 
            switch (pn2->pn_type) {
5354
 
              case TOK_NAME:
5355
 
                if (pn2->pn_op != JSOP_SETNAME) {
5356
 
                    EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR)
5357
 
                                       ? JSOP_GETGVAR
5358
 
                                       : (pn2->pn_op == JSOP_SETARG)
5359
 
                                       ? JSOP_GETARG
5360
 
                                       : (pn2->pn_op == JSOP_SETLOCAL)
5361
 
                                       ? JSOP_GETLOCAL
5362
 
                                       : JSOP_GETVAR,
5363
 
                                       atomIndex);
5364
 
                    break;
5365
 
                }
5366
 
                /* FALL THROUGH */
5367
 
              case TOK_DOT:
5368
 
                if (js_Emit1(cx, cg, JSOP_DUP) < 0)
5369
 
                    return JS_FALSE;
5370
 
                EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME)
5371
 
                                   ? JSOP_GETXPROP
5372
 
                                   : JSOP_GETPROP,
5373
 
                                   atomIndex);
5374
 
                break;
5375
 
              case TOK_LB:
5376
 
#if JS_HAS_LVALUE_RETURN
5377
 
              case TOK_LP:
5378
 
#endif
5379
 
#if JS_HAS_XML_SUPPORT
5380
 
              case TOK_UNARYOP:
5381
 
#endif
5382
 
                if (js_Emit1(cx, cg, JSOP_DUP2) < 0)
5383
 
                    return JS_FALSE;
5384
 
                if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
5385
 
                    return JS_FALSE;
5386
 
                break;
5387
 
              default:;
5388
 
            }
5389
 
        }
5390
 
 
5391
 
        /* Now emit the right operand (it may affect the namespace). */
5392
 
        if (!js_EmitTree(cx, cg, pn->pn_right))
5393
 
            return JS_FALSE;
5394
 
 
5395
 
        /* If += etc., emit the binary operator with a decompiler note. */
5396
 
        if (op != JSOP_NOP) {
5397
 
            /*
5398
 
             * Take care to avoid SRC_ASSIGNOP if the left-hand side is a
5399
 
             * const declared in a function (i.e., with non-negative pn_slot
5400
 
             * and JSPROP_READONLY in pn_attrs), as in this case (just a bit
5401
 
             * further below) we will avoid emitting the assignment op.
5402
 
             */
5403
 
            if (pn2->pn_type != TOK_NAME ||
5404
 
                pn2->pn_slot < 0 ||
5405
 
                !(pn2->pn_attrs & JSPROP_READONLY)) {
5406
 
                if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0)
5407
 
                    return JS_FALSE;
5408
 
            }
5409
 
            if (js_Emit1(cx, cg, op) < 0)
5410
 
                return JS_FALSE;
5411
 
        }
5412
 
 
5413
 
        /* Left parts such as a.b.c and a[b].c need a decompiler note. */
5414
 
        if (pn2->pn_type != TOK_NAME &&
5415
 
#if JS_HAS_DESTRUCTURING
5416
 
            pn2->pn_type != TOK_RB &&
5417
 
            pn2->pn_type != TOK_RC &&
5418
 
#endif
5419
 
            js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op),
5420
 
                           CG_OFFSET(cg) - top) < 0) {
5421
 
            return JS_FALSE;
5422
 
        }
5423
 
 
5424
 
        /* Finally, emit the specialized assignment bytecode. */
5425
 
        switch (pn2->pn_type) {
5426
 
          case TOK_NAME:
5427
 
            if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) {
5428
 
                if (pn2->pn_slot >= 0) {
5429
 
                    EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex);
5430
 
                } else {
5431
 
          case TOK_DOT:
5432
 
                    EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
5433
 
                }
5434
 
            }
5435
 
            break;
5436
 
          case TOK_LB:
5437
 
#if JS_HAS_LVALUE_RETURN
5438
 
          case TOK_LP:
5439
 
#endif
5440
 
            if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
5441
 
                return JS_FALSE;
5442
 
            break;
5443
 
#if JS_HAS_DESTRUCTURING
5444
 
          case TOK_RB:
5445
 
          case TOK_RC:
5446
 
            if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2))
5447
 
                return JS_FALSE;
5448
 
            break;
5449
 
#endif
5450
 
#if JS_HAS_XML_SUPPORT
5451
 
          case TOK_UNARYOP:
5452
 
            if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0)
5453
 
                return JS_FALSE;
5454
 
            break;
5455
 
#endif
5456
 
          default:
5457
 
            JS_ASSERT(0);
5458
 
        }
5459
 
        break;
5460
 
 
5461
 
      case TOK_HOOK:
5462
 
        /* Emit the condition, then branch if false to the else part. */
5463
 
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
5464
 
            return JS_FALSE;
5465
 
        noteIndex = js_NewSrcNote(cx, cg, SRC_COND);
5466
 
        if (noteIndex < 0)
5467
 
            return JS_FALSE;
5468
 
        beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
5469
 
        if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2))
5470
 
            return JS_FALSE;
5471
 
 
5472
 
        /* Jump around else, fixup the branch, emit else, fixup jump. */
5473
 
        jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
5474
 
        if (jmp < 0)
5475
 
            return JS_FALSE;
5476
 
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
5477
 
 
5478
 
        /*
5479
 
         * Because each branch pushes a single value, but our stack budgeting
5480
 
         * analysis ignores branches, we now have to adjust cg->stackDepth to
5481
 
         * ignore the value pushed by the first branch.  Execution will follow
5482
 
         * only one path, so we must decrement cg->stackDepth.
5483
 
         *
5484
 
         * Failing to do this will foil code, such as the try/catch/finally
5485
 
         * exception handling code generator, that samples cg->stackDepth for
5486
 
         * use at runtime (JSOP_SETSP), or in let expression and block code
5487
 
         * generation, which must use the stack depth to compute local stack
5488
 
         * indexes correctly.
5489
 
         */
5490
 
        JS_ASSERT(cg->stackDepth > 0);
5491
 
        cg->stackDepth--;
5492
 
        if (!js_EmitTree(cx, cg, pn->pn_kid3))
5493
 
            return JS_FALSE;
5494
 
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
5495
 
        if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
5496
 
            return JS_FALSE;
5497
 
        break;
5498
 
 
5499
 
      case TOK_OR:
5500
 
      case TOK_AND:
5501
 
        /*
5502
 
         * JSOP_OR converts the operand on the stack to boolean, and if true,
5503
 
         * leaves the original operand value on the stack and jumps; otherwise
5504
 
         * it pops and falls into the next bytecode, which evaluates the right
5505
 
         * operand.  The jump goes around the right operand evaluation.
5506
 
         *
5507
 
         * JSOP_AND converts the operand on the stack to boolean, and if false,
5508
 
         * leaves the original operand value on the stack and jumps; otherwise
5509
 
         * it pops and falls into the right operand's bytecode.
5510
 
         *
5511
 
         * Avoid tail recursion for long ||...|| expressions and long &&...&&
5512
 
         * expressions or long mixtures of ||'s and &&'s that can easily blow
5513
 
         * the stack, by forward-linking and then backpatching all the JSOP_OR
5514
 
         * and JSOP_AND bytecodes' immediate jump-offset operands.
5515
 
         */
5516
 
        pn3 = pn;
5517
 
        if (!js_EmitTree(cx, cg, pn->pn_left))
5518
 
            return JS_FALSE;
5519
 
        top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
5520
 
        if (top < 0)
5521
 
            return JS_FALSE;
5522
 
        jmp = top;
5523
 
        pn2 = pn->pn_right;
5524
 
        while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) {
5525
 
            pn = pn2;
5526
 
            if (!js_EmitTree(cx, cg, pn->pn_left))
5527
 
                return JS_FALSE;
5528
 
            off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
5529
 
            if (off < 0)
5530
 
                return JS_FALSE;
5531
 
            if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp))
5532
 
                return JS_FALSE;
5533
 
            jmp = off;
5534
 
            pn2 = pn->pn_right;
5535
 
        }
5536
 
        if (!js_EmitTree(cx, cg, pn2))
5537
 
            return JS_FALSE;
5538
 
        off = CG_OFFSET(cg);
5539
 
        do {
5540
 
            pc = CG_CODE(cg, top);
5541
 
            tmp = GetJumpOffset(cg, pc);
5542
 
            CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
5543
 
            *pc = pn3->pn_op;
5544
 
            top += tmp;
5545
 
        } while ((pn3 = pn3->pn_right) != pn2);
5546
 
        break;
5547
 
 
5548
 
      case TOK_BITOR:
5549
 
      case TOK_BITXOR:
5550
 
      case TOK_BITAND:
5551
 
      case TOK_EQOP:
5552
 
      case TOK_RELOP:
5553
 
      case TOK_IN:
5554
 
      case TOK_INSTANCEOF:
5555
 
      case TOK_SHOP:
5556
 
      case TOK_PLUS:
5557
 
      case TOK_MINUS:
5558
 
      case TOK_STAR:
5559
 
      case TOK_DIVOP:
5560
 
        if (pn->pn_arity == PN_LIST) {
5561
 
            /* Left-associative operator chain: avoid too much recursion. */
5562
 
            pn2 = pn->pn_head;
5563
 
            if (!js_EmitTree(cx, cg, pn2))
5564
 
                return JS_FALSE;
5565
 
            op = pn->pn_op;
5566
 
            while ((pn2 = pn2->pn_next) != NULL) {
5567
 
                if (!js_EmitTree(cx, cg, pn2))
5568
 
                    return JS_FALSE;
5569
 
                if (js_Emit1(cx, cg, op) < 0)
5570
 
                    return JS_FALSE;
5571
 
            }
5572
 
        } else {
5573
 
#if JS_HAS_XML_SUPPORT
5574
 
            uintN oldflags;
5575
 
 
5576
 
      case TOK_DBLCOLON:
5577
 
            if (pn->pn_arity == PN_NAME) {
5578
 
                if (!js_EmitTree(cx, cg, pn->pn_expr))
5579
 
                    return JS_FALSE;
5580
 
                if (!EmitAtomOp(cx, pn, pn->pn_op, cg))
5581
 
                    return JS_FALSE;
5582
 
                break;
5583
 
            }
5584
 
 
5585
 
            /*
5586
 
             * Binary :: has a right operand that brackets arbitrary code,
5587
 
             * possibly including a let (a = b) ... expression.  We must clear
5588
 
             * TCF_IN_FOR_INIT to avoid mis-compiling such beasts.
5589
 
             */
5590
 
            oldflags = cg->treeContext.flags;
5591
 
            cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
5592
 
#endif
5593
 
 
5594
 
            /* Binary operators that evaluate both operands unconditionally. */
5595
 
            if (!js_EmitTree(cx, cg, pn->pn_left))
5596
 
                return JS_FALSE;
5597
 
            if (!js_EmitTree(cx, cg, pn->pn_right))
5598
 
                return JS_FALSE;
5599
 
#if JS_HAS_XML_SUPPORT
5600
 
            cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
5601
 
#endif
5602
 
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
5603
 
                return JS_FALSE;
5604
 
        }
5605
 
        break;
5606
 
 
5607
 
      case TOK_THROW:
5608
 
#if JS_HAS_XML_SUPPORT
5609
 
      case TOK_AT:
5610
 
      case TOK_DEFAULT:
5611
 
        JS_ASSERT(pn->pn_arity == PN_UNARY);
5612
 
        /* FALL THROUGH */
5613
 
#endif
5614
 
      case TOK_UNARYOP:
5615
 
      {
5616
 
        uintN oldflags;
5617
 
 
5618
 
        /* Unary op, including unary +/-. */
5619
 
        pn2 = pn->pn_kid;
5620
 
        op = pn->pn_op;
5621
 
        if (op == JSOP_TYPEOF) {
5622
 
            for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid)
5623
 
                continue;
5624
 
            if (pn3->pn_type != TOK_NAME)
5625
 
                op = JSOP_TYPEOFEXPR;
5626
 
        }
5627
 
        oldflags = cg->treeContext.flags;
5628
 
        cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
5629
 
        if (!js_EmitTree(cx, cg, pn2))
5630
 
            return JS_FALSE;
5631
 
        cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
5632
 
#if JS_HAS_XML_SUPPORT
5633
 
        if (op == JSOP_XMLNAME &&
5634
 
            js_NewSrcNote2(cx, cg, SRC_PCBASE,
5635
 
                           CG_OFFSET(cg) - pn2->pn_offset) < 0) {
5636
 
            return JS_FALSE;
5637
 
        }
5638
 
#endif
5639
 
        if (js_Emit1(cx, cg, op) < 0)
5640
 
            return JS_FALSE;
5641
 
        break;
5642
 
      }
5643
 
 
5644
 
      case TOK_INC:
5645
 
      case TOK_DEC:
5646
 
      {
5647
 
        intN depth;
5648
 
 
5649
 
        /* Emit lvalue-specialized code for ++/-- operators. */
5650
 
        pn2 = pn->pn_kid;
5651
 
        JS_ASSERT(pn2->pn_type != TOK_RP);
5652
 
        op = pn->pn_op;
5653
 
        depth = cg->stackDepth;
5654
 
        switch (pn2->pn_type) {
5655
 
          case TOK_NAME:
5656
 
            pn2->pn_op = op;
5657
 
            if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
5658
 
                return JS_FALSE;
5659
 
            op = pn2->pn_op;
5660
 
            if (pn2->pn_slot >= 0) {
5661
 
                if (pn2->pn_attrs & JSPROP_READONLY) {
5662
 
                    /* Incrementing a declared const: just get its value. */
5663
 
                    op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST)
5664
 
                         ? JSOP_GETGVAR
5665
 
                         : JSOP_GETVAR;
5666
 
                }
5667
 
                atomIndex = (jsatomid) pn2->pn_slot;
5668
 
                EMIT_UINT16_IMM_OP(op, atomIndex);
5669
 
            } else {
5670
 
                if (!EmitAtomOp(cx, pn2, op, cg))
5671
 
                    return JS_FALSE;
5672
 
            }
5673
 
            break;
5674
 
          case TOK_DOT:
5675
 
            if (!EmitPropOp(cx, pn2, op, cg))
5676
 
                return JS_FALSE;
5677
 
            ++depth;
5678
 
            break;
5679
 
          case TOK_LB:
5680
 
            if (!EmitElemOp(cx, pn2, op, cg))
5681
 
                return JS_FALSE;
5682
 
            depth += 2;
5683
 
            break;
5684
 
#if JS_HAS_LVALUE_RETURN
5685
 
          case TOK_LP:
5686
 
            if (!js_EmitTree(cx, cg, pn2))
5687
 
                return JS_FALSE;
5688
 
            depth = cg->stackDepth;
5689
 
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
5690
 
                               CG_OFFSET(cg) - pn2->pn_offset) < 0) {
5691
 
                return JS_FALSE;
5692
 
            }
5693
 
            if (js_Emit1(cx, cg, op) < 0)
5694
 
                return JS_FALSE;
5695
 
            break;
5696
 
#endif
5697
 
#if JS_HAS_XML_SUPPORT
5698
 
          case TOK_UNARYOP:
5699
 
            JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
5700
 
            if (!js_EmitTree(cx, cg, pn2->pn_kid))
5701
 
                return JS_FALSE;
5702
 
            if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0)
5703
 
                return JS_FALSE;
5704
 
            depth = cg->stackDepth;
5705
 
            if (js_Emit1(cx, cg, op) < 0)
5706
 
                return JS_FALSE;
5707
 
            break;
5708
 
#endif
5709
 
          default:
5710
 
            JS_ASSERT(0);
5711
 
        }
5712
 
 
5713
 
        /*
5714
 
         * Allocate another stack slot for GC protection in case the initial
5715
 
         * value being post-incremented or -decremented is not a number, but
5716
 
         * converts to a jsdouble.  In the TOK_NAME cases, op has 0 operand
5717
 
         * uses and 1 definition, so we don't need an extra stack slot -- we
5718
 
         * can use the one allocated for the def.
5719
 
         */
5720
 
        if (pn2->pn_type != TOK_NAME &&
5721
 
            (js_CodeSpec[op].format & JOF_POST) &&
5722
 
            (uintN)depth == cg->maxStackDepth) {
5723
 
            ++cg->maxStackDepth;
5724
 
        }
5725
 
        break;
5726
 
      }
5727
 
 
5728
 
      case TOK_DELETE:
5729
 
        /*
5730
 
         * Under ECMA 3, deleting a non-reference returns true -- but alas we
5731
 
         * must evaluate the operand if it appears it might have side effects.
5732
 
         */
5733
 
        pn2 = pn->pn_kid;
5734
 
        switch (pn2->pn_type) {
5735
 
          case TOK_NAME:
5736
 
            pn2->pn_op = JSOP_DELNAME;
5737
 
            if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
5738
 
                return JS_FALSE;
5739
 
            op = pn2->pn_op;
5740
 
            if (op == JSOP_FALSE) {
5741
 
                if (js_Emit1(cx, cg, op) < 0)
5742
 
                    return JS_FALSE;
5743
 
            } else {
5744
 
                if (!EmitAtomOp(cx, pn2, op, cg))
5745
 
                    return JS_FALSE;
5746
 
            }
5747
 
            break;
5748
 
          case TOK_DOT:
5749
 
            if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))
5750
 
                return JS_FALSE;
5751
 
            break;
5752
 
#if JS_HAS_XML_SUPPORT
5753
 
          case TOK_DBLDOT:
5754
 
            if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg))
5755
 
                return JS_FALSE;
5756
 
            break;
5757
 
#endif
5758
 
#if JS_HAS_LVALUE_RETURN
5759
 
          case TOK_LP:
5760
 
            if (pn2->pn_op != JSOP_SETCALL) {
5761
 
                JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
5762
 
                pn2->pn_op = JSOP_SETCALL;
5763
 
            }
5764
 
            top = CG_OFFSET(cg);
5765
 
            if (!js_EmitTree(cx, cg, pn2))
5766
 
                return JS_FALSE;
5767
 
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
5768
 
                return JS_FALSE;
5769
 
            if (js_Emit1(cx, cg, JSOP_DELELEM) < 0)
5770
 
                return JS_FALSE;
5771
 
            break;
5772
 
#endif
5773
 
          case TOK_LB:
5774
 
            if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))
5775
 
                return JS_FALSE;
5776
 
            break;
5777
 
          default:
5778
 
            /*
5779
 
             * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
5780
 
             * to foo(), true (a comma expression, requiring SRC_PCDELTA).
5781
 
             */
5782
 
            useful = JS_FALSE;
5783
 
            if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
5784
 
                return JS_FALSE;
5785
 
            if (!useful) {
5786
 
                off = noteIndex = -1;
5787
 
            } else {
5788
 
                if (!js_EmitTree(cx, cg, pn2))
5789
 
                    return JS_FALSE;
5790
 
                off = CG_OFFSET(cg);
5791
 
                noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
5792
 
                if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
5793
 
                    return JS_FALSE;
5794
 
            }
5795
 
            if (js_Emit1(cx, cg, JSOP_TRUE) < 0)
5796
 
                return JS_FALSE;
5797
 
            if (noteIndex >= 0) {
5798
 
                tmp = CG_OFFSET(cg);
5799
 
                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
5800
 
                    return JS_FALSE;
5801
 
            }
5802
 
        }
5803
 
        break;
5804
 
 
5805
 
#if JS_HAS_XML_SUPPORT
5806
 
      case TOK_FILTER:
5807
 
        if (!js_EmitTree(cx, cg, pn->pn_left))
5808
 
            return JS_FALSE;
5809
 
        jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0);
5810
 
        if (jmp < 0)
5811
 
            return JS_FALSE;
5812
 
        if (!js_EmitTree(cx, cg, pn->pn_right))
5813
 
            return JS_FALSE;
5814
 
        if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0)
5815
 
            return JS_FALSE;
5816
 
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
5817
 
        break;
5818
 
#endif
5819
 
 
5820
 
      case TOK_DOT:
5821
 
        /*
5822
 
         * Pop a stack operand, convert it to object, get a property named by
5823
 
         * this bytecode's immediate-indexed atom operand, and push its value
5824
 
         * (not a reference to it).  This bytecode sets the virtual machine's
5825
 
         * "obj" register to the left operand's ToObject conversion result,
5826
 
         * for use by JSOP_PUSHOBJ.
5827
 
         */
5828
 
        ok = EmitPropOp(cx, pn, pn->pn_op, cg);
5829
 
        break;
5830
 
 
5831
 
      case TOK_LB:
5832
 
#if JS_HAS_XML_SUPPORT
5833
 
      case TOK_DBLDOT:
5834
 
#endif
5835
 
        /*
5836
 
         * Pop two operands, convert the left one to object and the right one
5837
 
         * to property name (atom or tagged int), get the named property, and
5838
 
         * push its value.  Set the "obj" register to the result of ToObject
5839
 
         * on the left operand.
5840
 
         */
5841
 
        ok = EmitElemOp(cx, pn, pn->pn_op, cg);
5842
 
        break;
5843
 
 
5844
 
      case TOK_NEW:
5845
 
      case TOK_LP:
5846
 
      {
5847
 
        uintN oldflags;
5848
 
 
5849
 
        /*
5850
 
         * Emit function call or operator new (constructor call) code.
5851
 
         * First, emit code for the left operand to evaluate the callable or
5852
 
         * constructable object expression.
5853
 
         *
5854
 
         * For E4X, if this expression is a dotted member reference, select
5855
 
         * JSOP_GETMETHOD instead of JSOP_GETPROP.  ECMA-357 separates XML
5856
 
         * method lookup from the normal property id lookup done for native
5857
 
         * objects.
5858
 
         */
5859
 
        pn2 = pn->pn_head;
5860
 
#if JS_HAS_XML_SUPPORT
5861
 
        if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) {
5862
 
            JS_ASSERT(pn2->pn_op == JSOP_GETPROP);
5863
 
            pn2->pn_op = JSOP_GETMETHOD;
5864
 
            pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE;
5865
 
        }
5866
 
#endif
5867
 
        if (!js_EmitTree(cx, cg, pn2))
5868
 
            return JS_FALSE;
5869
 
 
5870
 
        /*
5871
 
         * Push the virtual machine's "obj" register, which was set by a
5872
 
         * name, property, or element get (or set) bytecode.
5873
 
         */
5874
 
        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
5875
 
            return JS_FALSE;
5876
 
 
5877
 
        /* Remember start of callable-object bytecode for decompilation hint. */
5878
 
        off = top;
5879
 
 
5880
 
        /*
5881
 
         * Emit code for each argument in order, then emit the JSOP_*CALL or
5882
 
         * JSOP_NEW bytecode with a two-byte immediate telling how many args
5883
 
         * were pushed on the operand stack.
5884
 
         */
5885
 
        oldflags = cg->treeContext.flags;
5886
 
        cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
5887
 
        for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
5888
 
            if (!js_EmitTree(cx, cg, pn2))
5889
 
                return JS_FALSE;
5890
 
        }
5891
 
        cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
5892
 
        if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)
5893
 
            return JS_FALSE;
5894
 
 
5895
 
        argc = pn->pn_count - 1;
5896
 
        if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
5897
 
            return JS_FALSE;
5898
 
        break;
5899
 
      }
5900
 
 
5901
 
      case TOK_LEXICALSCOPE:
5902
 
      {
5903
 
        JSObject *obj;
5904
 
        jsint count;
5905
 
 
5906
 
        atom = pn->pn_atom;
5907
 
        obj = ATOM_TO_OBJECT(atom);
5908
 
        js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg));
5909
 
 
5910
 
        OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
5911
 
        count = OBJ_BLOCK_COUNT(cx, obj);
5912
 
        cg->stackDepth += count;
5913
 
        if ((uintN)cg->stackDepth > cg->maxStackDepth)
5914
 
            cg->maxStackDepth = cg->stackDepth;
5915
 
 
5916
 
        /*
5917
 
         * If this lexical scope is not for a catch block, let block or let
5918
 
         * expression, or any kind of for loop (where the scope starts in the
5919
 
         * head after the first part if for (;;), else in the body if for-in);
5920
 
         * and if our container is top-level but not a function body, or else
5921
 
         * a block statement; then emit a SRC_BRACE note.  All other container
5922
 
         * statements get braces by default from the decompiler.
5923
 
         */
5924
 
        noteIndex = -1;
5925
 
        type = pn->pn_expr->pn_type;
5926
 
        if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR &&
5927
 
            (!(stmt = stmtInfo.down)
5928
 
             ? !(cg->treeContext.flags & TCF_IN_FUNCTION)
5929
 
             : stmt->type == STMT_BLOCK)) {
5930
 
#if defined DEBUG_brendan || defined DEBUG_mrbkap
5931
 
            /* There must be no source note already output for the next op. */
5932
 
            JS_ASSERT(CG_NOTE_COUNT(cg) == 0 ||
5933
 
                      CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) ||
5934
 
                      !GettableNoteForNextOp(cg));
5935
 
#endif
5936
 
            noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
5937
 
            if (noteIndex < 0)
5938
 
                return JS_FALSE;
5939
 
        }
5940
 
 
5941
 
        ale = js_IndexAtom(cx, atom, &cg->atomList);
5942
 
        if (!ale)
5943
 
            return JS_FALSE;
5944
 
        JS_ASSERT(CG_OFFSET(cg) == top);
5945
 
        EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));
5946
 
 
5947
 
        if (!js_EmitTree(cx, cg, pn->pn_expr))
5948
 
            return JS_FALSE;
5949
 
 
5950
 
        op = pn->pn_op;
5951
 
        if (op == JSOP_LEAVEBLOCKEXPR) {
5952
 
            if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
5953
 
                return JS_FALSE;
5954
 
        } else {
5955
 
            if (noteIndex >= 0 &&
5956
 
                !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
5957
 
                                     CG_OFFSET(cg) - top)) {
5958
 
                return JS_FALSE;
5959
 
            }
5960
 
        }
5961
 
 
5962
 
        /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */
5963
 
        EMIT_UINT16_IMM_OP(op, count);
5964
 
        cg->stackDepth -= count;
5965
 
 
5966
 
        ok = js_PopStatementCG(cx, cg);
5967
 
        break;
5968
 
      }
5969
 
 
5970
 
#if JS_HAS_BLOCK_SCOPE
5971
 
      case TOK_LET:
5972
 
        /* Let statements have their variable declarations on the left. */
5973
 
        if (pn->pn_arity == PN_BINARY) {
5974
 
            pn2 = pn->pn_right;
5975
 
            pn = pn->pn_left;
5976
 
        } else {
5977
 
            pn2 = NULL;
5978
 
        }
5979
 
 
5980
 
        /* Non-null pn2 means that pn is the variable list from a let head. */
5981
 
        JS_ASSERT(pn->pn_arity == PN_LIST);
5982
 
        if (!EmitVariables(cx, cg, pn, pn2 != NULL, &noteIndex))
5983
 
            return JS_FALSE;
5984
 
 
5985
 
        /* Thus non-null pn2 is the body of the let block or expression. */
5986
 
        tmp = CG_OFFSET(cg);
5987
 
        if (pn2 && !js_EmitTree(cx, cg, pn2))
5988
 
            return JS_FALSE;
5989
 
 
5990
 
        if (noteIndex >= 0 &&
5991
 
            !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
5992
 
                                 CG_OFFSET(cg) - tmp)) {
5993
 
            return JS_FALSE;
5994
 
        }
5995
 
        break;
5996
 
#endif /* JS_HAS_BLOCK_SCOPE */
5997
 
 
5998
 
#if JS_HAS_GENERATORS
5999
 
       case TOK_ARRAYPUSH:
6000
 
        /*
6001
 
         * The array object's stack index is in cg->arrayCompSlot.  See below
6002
 
         * under the array initialiser code generator for array comprehension
6003
 
         * special casing.
6004
 
         */
6005
 
        if (!js_EmitTree(cx, cg, pn->pn_kid))
6006
 
            return JS_FALSE;
6007
 
        EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot);
6008
 
        break;
6009
 
#endif
6010
 
 
6011
 
      case TOK_RB:
6012
 
#if JS_HAS_GENERATORS
6013
 
      case TOK_ARRAYCOMP:
6014
 
#endif
6015
 
        /*
6016
 
         * Emit code for [a, b, c] of the form:
6017
 
         *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
6018
 
         * but use a stack slot for t and avoid dup'ing and popping it via
6019
 
         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
6020
 
         */
6021
 
        ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList);
6022
 
        if (!ale)
6023
 
            return JS_FALSE;
6024
 
        EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
6025
 
        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
6026
 
            return JS_FALSE;
6027
 
        if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
6028
 
            return JS_FALSE;
6029
 
 
6030
 
        pn2 = pn->pn_head;
6031
 
#if JS_HAS_SHARP_VARS
6032
 
        if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
6033
 
            EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
6034
 
            pn2 = pn2->pn_next;
6035
 
        }
6036
 
#endif
6037
 
 
6038
 
#if JS_HAS_GENERATORS
6039
 
        if (pn->pn_type == TOK_ARRAYCOMP) {
6040
 
            uintN saveSlot;
6041
 
 
6042
 
            /*
6043
 
             * Pass the new array's stack index to the TOK_ARRAYPUSH case by
6044
 
             * storing it in pn->pn_extra, then simply traverse the TOK_FOR
6045
 
             * node and its kids under pn2 to generate this comprehension.
6046
 
             */
6047
 
            JS_ASSERT(cg->stackDepth > 0);
6048
 
            saveSlot = cg->arrayCompSlot;
6049
 
            cg->arrayCompSlot = (uint32) (cg->stackDepth - 1);
6050
 
            if (!js_EmitTree(cx, cg, pn2))
6051
 
                return JS_FALSE;
6052
 
            cg->arrayCompSlot = saveSlot;
6053
 
 
6054
 
            /* Emit the usual op needed for decompilation. */
6055
 
            if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
6056
 
                return JS_FALSE;
6057
 
            break;
6058
 
        }
6059
 
#endif /* JS_HAS_GENERATORS */
6060
 
 
6061
 
        for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
6062
 
            if (!EmitNumberOp(cx, atomIndex, cg))
6063
 
                return JS_FALSE;
6064
 
 
6065
 
            /* FIXME 260106: holes in a sparse initializer are void-filled. */
6066
 
            if (pn2->pn_type == TOK_COMMA) {
6067
 
                if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
6068
 
                    return JS_FALSE;
6069
 
            } else {
6070
 
                if (!js_EmitTree(cx, cg, pn2))
6071
 
                    return JS_FALSE;
6072
 
            }
6073
 
 
6074
 
            if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
6075
 
                return JS_FALSE;
6076
 
        }
6077
 
 
6078
 
        if (pn->pn_extra & PNX_ENDCOMMA) {
6079
 
            /* Emit a source note so we know to decompile an extra comma. */
6080
 
            if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
6081
 
                return JS_FALSE;
6082
 
        }
6083
 
 
6084
 
        /* Emit an op for sharp array cleanup and decompilation. */
6085
 
        if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
6086
 
            return JS_FALSE;
6087
 
        break;
6088
 
 
6089
 
      case TOK_RC:
6090
 
        /*
6091
 
         * Emit code for {p:a, '%q':b, 2:c} of the form:
6092
 
         *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;
6093
 
         * but use a stack slot for t and avoid dup'ing and popping it via
6094
 
         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
6095
 
         */
6096
 
        ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList);
6097
 
        if (!ale)
6098
 
            return JS_FALSE;
6099
 
        EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
6100
 
 
6101
 
        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
6102
 
            return JS_FALSE;
6103
 
        if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
6104
 
            return JS_FALSE;
6105
 
 
6106
 
        pn2 = pn->pn_head;
6107
 
#if JS_HAS_SHARP_VARS
6108
 
        if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
6109
 
            EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
6110
 
            pn2 = pn2->pn_next;
6111
 
        }
6112
 
#endif
6113
 
 
6114
 
        for (; pn2; pn2 = pn2->pn_next) {
6115
 
            /* Emit an index for t[2], else map an atom for t.p or t['%q']. */
6116
 
            pn3 = pn2->pn_left;
6117
 
            switch (pn3->pn_type) {
6118
 
              case TOK_NUMBER:
6119
 
                if (!EmitNumberOp(cx, pn3->pn_dval, cg))
6120
 
                    return JS_FALSE;
6121
 
                break;
6122
 
              case TOK_NAME:
6123
 
              case TOK_STRING:
6124
 
                ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
6125
 
                if (!ale)
6126
 
                    return JS_FALSE;
6127
 
                break;
6128
 
              default:
6129
 
                JS_ASSERT(0);
6130
 
            }
6131
 
 
6132
 
            /* Emit code for the property initializer. */
6133
 
            if (!js_EmitTree(cx, cg, pn2->pn_right))
6134
 
                return JS_FALSE;
6135
 
 
6136
 
#if JS_HAS_GETTER_SETTER
6137
 
            op = pn2->pn_op;
6138
 
            if (op == JSOP_GETTER || op == JSOP_SETTER) {
6139
 
                if (pn3->pn_type != TOK_NUMBER &&
6140
 
                    ALE_INDEX(ale) >= JS_BIT(16)) {
6141
 
                    ReportStatementTooLarge(cx, cg);
6142
 
                    return JS_FALSE;
6143
 
                }
6144
 
                if (js_Emit1(cx, cg, op) < 0)
6145
 
                    return JS_FALSE;
6146
 
            }
6147
 
#endif
6148
 
            /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
6149
 
            if (pn3->pn_type == TOK_NUMBER) {
6150
 
                if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
6151
 
                    return JS_FALSE;
6152
 
                if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
6153
 
                    return JS_FALSE;
6154
 
            } else {
6155
 
                EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
6156
 
            }
6157
 
        }
6158
 
 
6159
 
        /* Emit an op for sharpArray cleanup and decompilation. */
6160
 
        if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
6161
 
            return JS_FALSE;
6162
 
        break;
6163
 
 
6164
 
#if JS_HAS_SHARP_VARS
6165
 
      case TOK_DEFSHARP:
6166
 
        if (!js_EmitTree(cx, cg, pn->pn_kid))
6167
 
            return JS_FALSE;
6168
 
        EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);
6169
 
        break;
6170
 
 
6171
 
      case TOK_USESHARP:
6172
 
        EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
6173
 
        break;
6174
 
#endif /* JS_HAS_SHARP_VARS */
6175
 
 
6176
 
      case TOK_RP:
6177
 
      {
6178
 
        uintN oldflags;
6179
 
 
6180
 
        /*
6181
 
         * The node for (e) has e as its kid, enabling users who want to nest
6182
 
         * assignment expressions in conditions to avoid the error correction
6183
 
         * done by Condition (from x = y to x == y) by double-parenthesizing.
6184
 
         */
6185
 
        oldflags = cg->treeContext.flags;
6186
 
        cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
6187
 
        if (!js_EmitTree(cx, cg, pn->pn_kid))
6188
 
            return JS_FALSE;
6189
 
        cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
6190
 
        if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
6191
 
            return JS_FALSE;
6192
 
        break;
6193
 
      }
6194
 
 
6195
 
      case TOK_NAME:
6196
 
        if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE))
6197
 
            return JS_FALSE;
6198
 
        op = pn->pn_op;
6199
 
        if (op == JSOP_ARGUMENTS) {
6200
 
            if (js_Emit1(cx, cg, op) < 0)
6201
 
                return JS_FALSE;
6202
 
            break;
6203
 
        }
6204
 
        if (pn->pn_slot >= 0) {
6205
 
            atomIndex = (jsatomid) pn->pn_slot;
6206
 
            EMIT_UINT16_IMM_OP(op, atomIndex);
6207
 
            break;
6208
 
        }
6209
 
        /* FALL THROUGH */
6210
 
 
6211
 
#if JS_HAS_XML_SUPPORT
6212
 
      case TOK_XMLATTR:
6213
 
      case TOK_XMLSPACE:
6214
 
      case TOK_XMLTEXT:
6215
 
      case TOK_XMLCDATA:
6216
 
      case TOK_XMLCOMMENT:
6217
 
#endif
6218
 
      case TOK_STRING:
6219
 
      case TOK_OBJECT:
6220
 
        /*
6221
 
         * The scanner and parser associate JSOP_NAME with TOK_NAME, although
6222
 
         * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME,
6223
 
         * JSOP_FORNAME, etc.).  Among JSOP_*NAME* variants, only JSOP_NAME
6224
 
         * may generate the first operand of a call or new expression, so only
6225
 
         * it sets the "obj" virtual machine register to the object along the
6226
 
         * scope chain in which the name was found.
6227
 
         *
6228
 
         * Token types for STRING and OBJECT have corresponding bytecode ops
6229
 
         * in pn_op and emit the same format as NAME, so they share this code.
6230
 
         */
6231
 
        ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
6232
 
        break;
6233
 
 
6234
 
      case TOK_NUMBER:
6235
 
        ok = EmitNumberOp(cx, pn->pn_dval, cg);
6236
 
        break;
6237
 
 
6238
 
#if JS_HAS_XML_SUPPORT
6239
 
      case TOK_ANYNAME:
6240
 
#endif
6241
 
      case TOK_PRIMARY:
6242
 
        if (js_Emit1(cx, cg, pn->pn_op) < 0)
6243
 
            return JS_FALSE;
6244
 
        break;
6245
 
 
6246
 
#if JS_HAS_DEBUGGER_KEYWORD
6247
 
      case TOK_DEBUGGER:
6248
 
        if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0)
6249
 
            return JS_FALSE;
6250
 
        break;
6251
 
#endif /* JS_HAS_DEBUGGER_KEYWORD */
6252
 
 
6253
 
#if JS_HAS_XML_SUPPORT
6254
 
      case TOK_XMLELEM:
6255
 
      case TOK_XMLLIST:
6256
 
        if (pn->pn_op == JSOP_XMLOBJECT) {
6257
 
            ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
6258
 
            break;
6259
 
        }
6260
 
 
6261
 
        JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
6262
 
        switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) {
6263
 
          case TOK_XMLETAGO:
6264
 
            JS_ASSERT(0);
6265
 
            /* FALL THROUGH */
6266
 
          case TOK_XMLPTAGC:
6267
 
          case TOK_XMLSTAGO:
6268
 
            break;
6269
 
          default:
6270
 
            if (js_Emit1(cx, cg, JSOP_STARTXML) < 0)
6271
 
                return JS_FALSE;
6272
 
        }
6273
 
 
6274
 
        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6275
 
            if (pn2->pn_type == TOK_LC &&
6276
 
                js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
6277
 
                return JS_FALSE;
6278
 
            }
6279
 
            if (!js_EmitTree(cx, cg, pn2))
6280
 
                return JS_FALSE;
6281
 
            if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
6282
 
                return JS_FALSE;
6283
 
        }
6284
 
 
6285
 
        if (pn->pn_extra & PNX_XMLROOT) {
6286
 
            if (pn->pn_count == 0) {
6287
 
                JS_ASSERT(pn->pn_type == TOK_XMLLIST);
6288
 
                atom = cx->runtime->atomState.emptyAtom;
6289
 
                ale = js_IndexAtom(cx, atom, &cg->atomList);
6290
 
                if (!ale)
6291
 
                    return JS_FALSE;
6292
 
                EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
6293
 
            }
6294
 
            if (js_Emit1(cx, cg, pn->pn_op) < 0)
6295
 
                return JS_FALSE;
6296
 
        }
6297
 
#ifdef DEBUG
6298
 
        else
6299
 
            JS_ASSERT(pn->pn_count != 0);
6300
 
#endif
6301
 
        break;
6302
 
 
6303
 
      case TOK_XMLPTAGC:
6304
 
        if (pn->pn_op == JSOP_XMLOBJECT) {
6305
 
            ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
6306
 
            break;
6307
 
        }
6308
 
        /* FALL THROUGH */
6309
 
 
6310
 
      case TOK_XMLSTAGO:
6311
 
      case TOK_XMLETAGO:
6312
 
      {
6313
 
        uint32 i;
6314
 
 
6315
 
        if (js_Emit1(cx, cg, JSOP_STARTXML) < 0)
6316
 
            return JS_FALSE;
6317
 
 
6318
 
        ale = js_IndexAtom(cx,
6319
 
                           (pn->pn_type == TOK_XMLETAGO)
6320
 
                           ? cx->runtime->atomState.etagoAtom
6321
 
                           : cx->runtime->atomState.stagoAtom,
6322
 
                           &cg->atomList);
6323
 
        if (!ale)
6324
 
            return JS_FALSE;
6325
 
        EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
6326
 
 
6327
 
        JS_ASSERT(pn->pn_count != 0);
6328
 
        pn2 = pn->pn_head;
6329
 
        if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0)
6330
 
            return JS_FALSE;
6331
 
        if (!js_EmitTree(cx, cg, pn2))
6332
 
            return JS_FALSE;
6333
 
        if (js_Emit1(cx, cg, JSOP_ADD) < 0)
6334
 
            return JS_FALSE;
6335
 
 
6336
 
        for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) {
6337
 
            if (pn2->pn_type == TOK_LC &&
6338
 
                js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
6339
 
                return JS_FALSE;
6340
 
            }
6341
 
            if (!js_EmitTree(cx, cg, pn2))
6342
 
                return JS_FALSE;
6343
 
            if ((i & 1) && pn2->pn_type == TOK_LC) {
6344
 
                if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0)
6345
 
                    return JS_FALSE;
6346
 
            }
6347
 
            if (js_Emit1(cx, cg,
6348
 
                         (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) {
6349
 
                return JS_FALSE;
6350
 
            }
6351
 
        }
6352
 
 
6353
 
        ale = js_IndexAtom(cx,
6354
 
                           (pn->pn_type == TOK_XMLPTAGC)
6355
 
                           ? cx->runtime->atomState.ptagcAtom
6356
 
                           : cx->runtime->atomState.tagcAtom,
6357
 
                           &cg->atomList);
6358
 
        if (!ale)
6359
 
            return JS_FALSE;
6360
 
        EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
6361
 
        if (js_Emit1(cx, cg, JSOP_ADD) < 0)
6362
 
            return JS_FALSE;
6363
 
 
6364
 
        if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0)
6365
 
            return JS_FALSE;
6366
 
        break;
6367
 
      }
6368
 
 
6369
 
      case TOK_XMLNAME:
6370
 
        if (pn->pn_arity == PN_LIST) {
6371
 
            JS_ASSERT(pn->pn_count != 0);
6372
 
            for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6373
 
                if (!js_EmitTree(cx, cg, pn2))
6374
 
                    return JS_FALSE;
6375
 
                if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
6376
 
                    return JS_FALSE;
6377
 
            }
6378
 
        } else {
6379
 
            JS_ASSERT(pn->pn_arity == PN_NULLARY);
6380
 
            ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
6381
 
        }
6382
 
        break;
6383
 
 
6384
 
      case TOK_XMLPI:
6385
 
        ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList);
6386
 
        if (!ale)
6387
 
            return JS_FALSE;
6388
 
        if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg))
6389
 
            return JS_FALSE;
6390
 
        if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg))
6391
 
            return JS_FALSE;
6392
 
        break;
6393
 
#endif /* JS_HAS_XML_SUPPORT */
6394
 
 
6395
 
      default:
6396
 
        JS_ASSERT(0);
6397
 
    }
6398
 
 
6399
 
    if (ok && --cg->emitLevel == 0 && cg->spanDeps)
6400
 
        ok = OptimizeSpanDeps(cx, cg);
6401
 
 
6402
 
    return ok;
6403
 
}
6404
 
 
6405
 
/* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */
6406
 
JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
6407
 
    {"null",            0,      0,      0},
6408
 
    {"if",              0,      0,      0},
6409
 
    {"if-else",         2,      0,      1},
6410
 
    {"while",           1,      0,      1},
6411
 
    {"for",             3,      1,      1},
6412
 
    {"continue",        0,      0,      0},
6413
 
    {"decl",            1,      1,      1},
6414
 
    {"pcdelta",         1,      0,      1},
6415
 
    {"assignop",        0,      0,      0},
6416
 
    {"cond",            1,      0,      1},
6417
 
    {"brace",           1,      0,      1},
6418
 
    {"hidden",          0,      0,      0},
6419
 
    {"pcbase",          1,      0,     -1},
6420
 
    {"label",           1,      0,      0},
6421
 
    {"labelbrace",      1,      0,      0},
6422
 
    {"endbrace",        0,      0,      0},
6423
 
    {"break2label",     1,      0,      0},
6424
 
    {"cont2label",      1,      0,      0},
6425
 
    {"switch",          2,      0,      1},
6426
 
    {"funcdef",         1,      0,      0},
6427
 
    {"catch",           1,      0,      1},
6428
 
    {"extended",       -1,      0,      0},
6429
 
    {"newline",         0,      0,      0},
6430
 
    {"setline",         1,      0,      0},
6431
 
    {"xdelta",          0,      0,      0},
6432
 
};
6433
 
 
6434
 
static intN
6435
 
AllocSrcNote(JSContext *cx, JSCodeGenerator *cg)
6436
 
{
6437
 
    intN index;
6438
 
    JSArenaPool *pool;
6439
 
    size_t size;
6440
 
 
6441
 
    index = CG_NOTE_COUNT(cg);
6442
 
    if (((uintN)index & CG_NOTE_MASK(cg)) == 0) {
6443
 
        pool = cg->notePool;
6444
 
        size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
6445
 
        if (!CG_NOTES(cg)) {
6446
 
            /* Allocate the first note array lazily; leave noteMask alone. */
6447
 
            JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size);
6448
 
        } else {
6449
 
            /* Grow by doubling note array size; update noteMask on success. */
6450
 
            JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
6451
 
            if (CG_NOTES(cg))
6452
 
                CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
6453
 
        }
6454
 
        if (!CG_NOTES(cg)) {
6455
 
            JS_ReportOutOfMemory(cx);
6456
 
            return -1;
6457
 
        }
6458
 
    }
6459
 
 
6460
 
    CG_NOTE_COUNT(cg) = index + 1;
6461
 
    return index;
6462
 
}
6463
 
 
6464
 
intN
6465
 
js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
6466
 
{
6467
 
    intN index, n;
6468
 
    jssrcnote *sn;
6469
 
    ptrdiff_t offset, delta, xdelta;
6470
 
 
6471
 
    /*
6472
 
     * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then
6473
 
     * incrementing CG_NOTE_COUNT(cg).
6474
 
     */
6475
 
    index = AllocSrcNote(cx, cg);
6476
 
    if (index < 0)
6477
 
        return -1;
6478
 
    sn = &CG_NOTES(cg)[index];
6479
 
 
6480
 
    /*
6481
 
     * Compute delta from the last annotated bytecode's offset.  If it's too
6482
 
     * big to fit in sn, allocate one or more xdelta notes and reset sn.
6483
 
     */
6484
 
    offset = CG_OFFSET(cg);
6485
 
    delta = offset - CG_LAST_NOTE_OFFSET(cg);
6486
 
    CG_LAST_NOTE_OFFSET(cg) = offset;
6487
 
    if (delta >= SN_DELTA_LIMIT) {
6488
 
        do {
6489
 
            xdelta = JS_MIN(delta, SN_XDELTA_MASK);
6490
 
            SN_MAKE_XDELTA(sn, xdelta);
6491
 
            delta -= xdelta;
6492
 
            index = AllocSrcNote(cx, cg);
6493
 
            if (index < 0)
6494
 
                return -1;
6495
 
            sn = &CG_NOTES(cg)[index];
6496
 
        } while (delta >= SN_DELTA_LIMIT);
6497
 
    }
6498
 
 
6499
 
    /*
6500
 
     * Initialize type and delta, then allocate the minimum number of notes
6501
 
     * needed for type's arity.  Usually, we won't need more, but if an offset
6502
 
     * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg).
6503
 
     */
6504
 
    SN_MAKE_NOTE(sn, type, delta);
6505
 
    for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) {
6506
 
        if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
6507
 
            return -1;
6508
 
    }
6509
 
    return index;
6510
 
}
6511
 
 
6512
 
intN
6513
 
js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
6514
 
               ptrdiff_t offset)
6515
 
{
6516
 
    intN index;
6517
 
 
6518
 
    index = js_NewSrcNote(cx, cg, type);
6519
 
    if (index >= 0) {
6520
 
        if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
6521
 
            return -1;
6522
 
    }
6523
 
    return index;
6524
 
}
6525
 
 
6526
 
intN
6527
 
js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
6528
 
               ptrdiff_t offset1, ptrdiff_t offset2)
6529
 
{
6530
 
    intN index;
6531
 
 
6532
 
    index = js_NewSrcNote(cx, cg, type);
6533
 
    if (index >= 0) {
6534
 
        if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1))
6535
 
            return -1;
6536
 
        if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2))
6537
 
            return -1;
6538
 
    }
6539
 
    return index;
6540
 
}
6541
 
 
6542
 
static JSBool
6543
 
GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg)
6544
 
{
6545
 
    JSArenaPool *pool;
6546
 
    size_t size;
6547
 
 
6548
 
    /* Grow by doubling note array size; update noteMask on success. */
6549
 
    pool = cg->notePool;
6550
 
    size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
6551
 
    JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
6552
 
    if (!CG_NOTES(cg)) {
6553
 
        JS_ReportOutOfMemory(cx);
6554
 
        return JS_FALSE;
6555
 
    }
6556
 
    CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
6557
 
    return JS_TRUE;
6558
 
}
6559
 
 
6560
 
jssrcnote *
6561
 
js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
6562
 
                     ptrdiff_t delta)
6563
 
{
6564
 
    ptrdiff_t base, limit, newdelta, diff;
6565
 
    intN index;
6566
 
 
6567
 
    /*
6568
 
     * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to
6569
 
     * main script note deltas, and only by a small positive amount.
6570
 
     */
6571
 
    JS_ASSERT(cg->current == &cg->main);
6572
 
    JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
6573
 
 
6574
 
    base = SN_DELTA(sn);
6575
 
    limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
6576
 
    newdelta = base + delta;
6577
 
    if (newdelta < limit) {
6578
 
        SN_SET_DELTA(sn, newdelta);
6579
 
    } else {
6580
 
        index = sn - cg->main.notes;
6581
 
        if ((cg->main.noteCount & cg->main.noteMask) == 0) {
6582
 
            if (!GrowSrcNotes(cx, cg))
6583
 
                return NULL;
6584
 
            sn = cg->main.notes + index;
6585
 
        }
6586
 
        diff = cg->main.noteCount - index;
6587
 
        cg->main.noteCount++;
6588
 
        memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
6589
 
        SN_MAKE_XDELTA(sn, delta);
6590
 
        sn++;
6591
 
    }
6592
 
    return sn;
6593
 
}
6594
 
 
6595
 
JS_FRIEND_API(uintN)
6596
 
js_SrcNoteLength(jssrcnote *sn)
6597
 
{
6598
 
    uintN arity;
6599
 
    jssrcnote *base;
6600
 
 
6601
 
    arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity;
6602
 
    for (base = sn++; arity; sn++, arity--) {
6603
 
        if (*sn & SN_3BYTE_OFFSET_FLAG)
6604
 
            sn += 2;
6605
 
    }
6606
 
    return sn - base;
6607
 
}
6608
 
 
6609
 
JS_FRIEND_API(ptrdiff_t)
6610
 
js_GetSrcNoteOffset(jssrcnote *sn, uintN which)
6611
 
{
6612
 
    /* Find the offset numbered which (i.e., skip exactly which offsets). */
6613
 
    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
6614
 
    JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
6615
 
    for (sn++; which; sn++, which--) {
6616
 
        if (*sn & SN_3BYTE_OFFSET_FLAG)
6617
 
            sn += 2;
6618
 
    }
6619
 
    if (*sn & SN_3BYTE_OFFSET_FLAG) {
6620
 
        return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
6621
 
                           | (sn[1] << 8)
6622
 
                           | sn[2]);
6623
 
    }
6624
 
    return (ptrdiff_t)*sn;
6625
 
}
6626
 
 
6627
 
JSBool
6628
 
js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
6629
 
                    uintN which, ptrdiff_t offset)
6630
 
{
6631
 
    jssrcnote *sn;
6632
 
    ptrdiff_t diff;
6633
 
 
6634
 
    if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) {
6635
 
        ReportStatementTooLarge(cx, cg);
6636
 
        return JS_FALSE;
6637
 
    }
6638
 
 
6639
 
    /* Find the offset numbered which (i.e., skip exactly which offsets). */
6640
 
    sn = &CG_NOTES(cg)[index];
6641
 
    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
6642
 
    JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
6643
 
    for (sn++; which; sn++, which--) {
6644
 
        if (*sn & SN_3BYTE_OFFSET_FLAG)
6645
 
            sn += 2;
6646
 
    }
6647
 
 
6648
 
    /* See if the new offset requires three bytes. */
6649
 
    if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
6650
 
        /* Maybe this offset was already set to a three-byte value. */
6651
 
        if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
6652
 
            /* Losing, need to insert another two bytes for this offset. */
6653
 
            index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote);
6654
 
 
6655
 
            /*
6656
 
             * Simultaneously test to see if the source note array must grow to
6657
 
             * accomodate either the first or second byte of additional storage
6658
 
             * required by this 3-byte offset.
6659
 
             */
6660
 
            if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {
6661
 
                if (!GrowSrcNotes(cx, cg))
6662
 
                    return JS_FALSE;
6663
 
                sn = CG_NOTES(cg) + index;
6664
 
            }
6665
 
            CG_NOTE_COUNT(cg) += 2;
6666
 
 
6667
 
            diff = CG_NOTE_COUNT(cg) - (index + 3);
6668
 
            JS_ASSERT(diff >= 0);
6669
 
            if (diff > 0)
6670
 
                memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
6671
 
        }
6672
 
        *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
6673
 
        *sn++ = (jssrcnote)(offset >> 8);
6674
 
    }
6675
 
    *sn = (jssrcnote)offset;
6676
 
    return JS_TRUE;
6677
 
}
6678
 
 
6679
 
#ifdef DEBUG_notme
6680
 
#define DEBUG_srcnotesize
6681
 
#endif
6682
 
 
6683
 
#ifdef DEBUG_srcnotesize
6684
 
#define NBINS 10
6685
 
static uint32 hist[NBINS];
6686
 
 
6687
 
void DumpSrcNoteSizeHist()
6688
 
{
6689
 
    static FILE *fp;
6690
 
    int i, n;
6691
 
 
6692
 
    if (!fp) {
6693
 
        fp = fopen("/tmp/srcnotes.hist", "w");
6694
 
        if (!fp)
6695
 
            return;
6696
 
        setvbuf(fp, NULL, _IONBF, 0);
6697
 
    }
6698
 
    fprintf(fp, "SrcNote size histogram:\n");
6699
 
    for (i = 0; i < NBINS; i++) {
6700
 
        fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]);
6701
 
        for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n)
6702
 
            fputc('*', fp);
6703
 
        fputc('\n', fp);
6704
 
    }
6705
 
    fputc('\n', fp);
6706
 
}
6707
 
#endif
6708
 
 
6709
 
/*
6710
 
 * Fill in the storage at notes with prolog and main srcnotes; the space at
6711
 
 * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h.
6712
 
 * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's
6713
 
 * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES!
6714
 
 */
6715
 
JSBool
6716
 
js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes)
6717
 
{
6718
 
    uintN prologCount, mainCount, totalCount;
6719
 
    ptrdiff_t offset, delta;
6720
 
    jssrcnote *sn;
6721
 
 
6722
 
    JS_ASSERT(cg->current == &cg->main);
6723
 
 
6724
 
    prologCount = cg->prolog.noteCount;
6725
 
    if (prologCount && cg->prolog.currentLine != cg->firstLine) {
6726
 
        CG_SWITCH_TO_PROLOG(cg);
6727
 
        if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0)
6728
 
            return JS_FALSE;
6729
 
        prologCount = cg->prolog.noteCount;
6730
 
        CG_SWITCH_TO_MAIN(cg);
6731
 
    } else {
6732
 
        /*
6733
 
         * Either no prolog srcnotes, or no line number change over prolog.
6734
 
         * We don't need a SRC_SETLINE, but we may need to adjust the offset
6735
 
         * of the first main note, by adding to its delta and possibly even
6736
 
         * prepending SRC_XDELTA notes to it to account for prolog bytecodes
6737
 
         * that came at and after the last annotated bytecode.
6738
 
         */
6739
 
        offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset;
6740
 
        JS_ASSERT(offset >= 0);
6741
 
        if (offset > 0 && cg->main.noteCount != 0) {
6742
 
            /* NB: Use as much of the first main note's delta as we can. */
6743
 
            sn = cg->main.notes;
6744
 
            delta = SN_IS_XDELTA(sn)
6745
 
                    ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
6746
 
                    : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
6747
 
            if (offset < delta)
6748
 
                delta = offset;
6749
 
            for (;;) {
6750
 
                if (!js_AddToSrcNoteDelta(cx, cg, sn, delta))
6751
 
                    return JS_FALSE;
6752
 
                offset -= delta;
6753
 
                if (offset == 0)
6754
 
                    break;
6755
 
                delta = JS_MIN(offset, SN_XDELTA_MASK);
6756
 
                sn = cg->main.notes;
6757
 
            }
6758
 
        }
6759
 
    }
6760
 
 
6761
 
    mainCount = cg->main.noteCount;
6762
 
    totalCount = prologCount + mainCount;
6763
 
    if (prologCount)
6764
 
        memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount));
6765
 
    memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
6766
 
    SN_MAKE_TERMINATOR(&notes[totalCount]);
6767
 
 
6768
 
#ifdef DEBUG_notme
6769
 
  { int bin = JS_CeilingLog2(totalCount);
6770
 
    if (bin >= NBINS)
6771
 
        bin = NBINS - 1;
6772
 
    ++hist[bin];
6773
 
  }
6774
 
#endif
6775
 
    return JS_TRUE;
6776
 
}
6777
 
 
6778
 
JSBool
6779
 
js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
6780
 
{
6781
 
    size_t size, incr;
6782
 
    ptrdiff_t delta;
6783
 
 
6784
 
    size = TRYNOTE_SIZE(cg->treeContext.tryCount);
6785
 
    if (size <= cg->tryNoteSpace)
6786
 
        return JS_TRUE;
6787
 
 
6788
 
    /*
6789
 
     * Allocate trynotes from cx->tempPool.
6790
 
     * XXX Too much growing and we bloat, as other tempPool allocators block
6791
 
     * in-place growth, and we never recycle old free space in an arena.
6792
 
     * YYY But once we consume an entire arena, we'll realloc it, letting the
6793
 
     * malloc heap recycle old space, while still freeing _en masse_ via the
6794
 
     * arena pool.
6795
 
     */
6796
 
    if (!cg->tryBase) {
6797
 
        size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK));
6798
 
        JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);
6799
 
        if (!cg->tryBase)
6800
 
            return JS_FALSE;
6801
 
        cg->tryNoteSpace = size;
6802
 
        cg->tryNext = cg->tryBase;
6803
 
    } else {
6804
 
        delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char);
6805
 
        incr = size - cg->tryNoteSpace;
6806
 
        incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK));
6807
 
        size = cg->tryNoteSpace;
6808
 
        JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr);
6809
 
        if (!cg->tryBase)
6810
 
            return JS_FALSE;
6811
 
        cg->tryNoteSpace = size + incr;
6812
 
        cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta);
6813
 
    }
6814
 
    return JS_TRUE;
6815
 
}
6816
 
 
6817
 
JSTryNote *
6818
 
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
6819
 
              ptrdiff_t end, ptrdiff_t catchStart)
6820
 
{
6821
 
    JSTryNote *tn;
6822
 
 
6823
 
    JS_ASSERT(cg->tryBase <= cg->tryNext);
6824
 
    JS_ASSERT(catchStart >= 0);
6825
 
    tn = cg->tryNext++;
6826
 
    tn->start = start;
6827
 
    tn->length = end - start;
6828
 
    tn->catchStart = catchStart;
6829
 
    return tn;
6830
 
}
6831
 
 
6832
 
void
6833
 
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
6834
 
{
6835
 
    uintN count;
6836
 
 
6837
 
    count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote);
6838
 
    if (!count)
6839
 
        return;
6840
 
 
6841
 
    memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count));
6842
 
    notes[count].start = 0;
6843
 
    notes[count].length = CG_OFFSET(cg);
6844
 
    notes[count].catchStart = 0;
6845
 
}