1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 /*
41  * JS bytecode generation.
42  */
43 #include "jsstddef.h"
44 #ifdef HAVE_MEMORY_H
45 #include <memory.h>
46 #endif
47 #include <string.h>
48 #include "jstypes.h"
49 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jsbit.h"
52 #include "jsprf.h"
53 #include "jsapi.h"
54 #include "jsatom.h"
55 #include "jscntxt.h"
56 #include "jsconfig.h"
57 #include "jsemit.h"
58 #include "jsfun.h"
59 #include "jsnum.h"
60 #include "jsopcode.h"
61 #include "jsparse.h"
62 #include "jsscan.h"
63 #include "jsscope.h"
64 #include "jsscript.h"
65
66 /* Allocation chunk counts, must be powers of two in general. */
67 #define BYTECODE_CHUNK  256     /* code allocation increment */
68 #define SRCNOTE_CHUNK   64      /* initial srcnote allocation increment */
69 #define TRYNOTE_CHUNK   64      /* trynote allocation increment */
70
71 /* Macros to compute byte sizes from typed element counts. */
72 #define BYTECODE_SIZE(n)        ((n) * sizeof(jsbytecode))
73 #define SRCNOTE_SIZE(n)         ((n) * sizeof(jssrcnote))
74 #define TRYNOTE_SIZE(n)         ((n) * sizeof(JSTryNote))
75
76 JS_FRIEND_API(JSBool)
77 js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
78                      JSArenaPool *codePool, JSArenaPool *notePool,
79                      const char *filename, uintN lineno,
80                      JSPrincipals *principals)
81 4920 {
82 4920     memset(cg, 0, sizeof *cg);
83 4920     TREE_CONTEXT_INIT(&cg->treeContext);
84 4920     cg->treeContext.flags |= TCF_COMPILING;
85 4920     cg->codePool = codePool;
86 4920     cg->notePool = notePool;
87 4920     cg->codeMark = JS_ARENA_MARK(codePool);
88 4920     cg->noteMark = JS_ARENA_MARK(notePool);
89 4920     cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
90 4920     cg->current = &cg->main;
91 4920     cg->filename = filename;
92 4920     cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno;
93 4920     cg->principals = principals;
94 4920     ATOM_LIST_INIT(&cg->atomList);
95 4920     cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1;
96 4920     ATOM_LIST_INIT(&cg->constList);
97 4920     return JS_TRUE;
98 }
99
100 JS_FRIEND_API(void)
101 js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
102 4920 {
103 4920     TREE_CONTEXT_FINISH(&cg->treeContext);
104 4920     JS_ARENA_RELEASE(cg->codePool, cg->codeMark);
105 4920     JS_ARENA_RELEASE(cg->notePool, cg->noteMark);
106 4920     JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);
107 }
108
109 static ptrdiff_t
110 EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)
111 1679699 {
112 1679699     jsbytecode *base, *limit, *next;
113 1679699     ptrdiff_t offset, length;
114 1679699     size_t incr, size;
115
116 1679699     base = CG_BASE(cg);
117 1679699     next = CG_NEXT(cg);
118 1679699     limit = CG_LIMIT(cg);
119 1679699     offset = PTRDIFF(next, base, jsbytecode);
120 1679699     if (next + delta > limit) {
121 10069         length = offset + delta;
122 10069         length = (length <= BYTECODE_CHUNK)
123                  ? BYTECODE_CHUNK
124                  : JS_BIT(JS_CeilingLog2(length));
125 10069         incr = BYTECODE_SIZE(length);
126 10069         if (!base) {
127 5194             JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
128         } else {
129 4875             size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
130 4875             incr -= size;
131 4875             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
132         }
133 10069         if (!base) {
134 0             JS_ReportOutOfMemory(cx);
135 0             return -1;
136         }
137 10069         CG_BASE(cg) = base;
138 10069         CG_LIMIT(cg) = base + length;
139 10069         CG_NEXT(cg) = base + offset;
140     }
141 1679699     return offset;
142 }
143
144 static void
145 UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
146 1679699 {
147 1679699     jsbytecode *pc;
148 1679699     const JSCodeSpec *cs;
149 1679699     intN nuses;
150
151 1679699     pc = CG_CODE(cg, target);
152 1679699     cs = &js_CodeSpec[pc[0]];
153 1679699     nuses = cs->nuses;
154 1679699     if (nuses < 0)
155 270828         nuses = 2 + GET_ARGC(pc);       /* stack: fun, this, [argc arguments] */
156 1679699     cg->stackDepth -= nuses;
157 1679699     if (cg->stackDepth < 0) {
158 0         char numBuf[12];
159 0         JS_snprintf(numBuf, sizeof numBuf, "%d", target);
160 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
161                              JSMSG_STACK_UNDERFLOW,
162                              cg->filename ? cg->filename : "stdin", numBuf);
163     }
164 1679699     cg->stackDepth += cs->ndefs;
165 1679699     if ((uintN)cg->stackDepth > cg->maxStackDepth)
166 22979         cg->maxStackDepth = cg->stackDepth;
167 }
168
169 ptrdiff_t
170 js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
171 666289 {
172 666289     ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
173
174 666289     if (offset >= 0) {
175 666289         *CG_NEXT(cg)++ = (jsbytecode)op;
176 666289         UpdateDepth(cx, cg, offset);
177     }
178 666289     return offset;
179 }
180
181 ptrdiff_t
182 js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)
183 0 {
184 0     ptrdiff_t offset = EmitCheck(cx, cg, op, 2);
185
186 0     if (offset >= 0) {
187 0         jsbytecode *next = CG_NEXT(cg);
188 0         next[0] = (jsbytecode)op;
189 0         next[1] = op1;
190 0         CG_NEXT(cg) = next + 2;
191 0         UpdateDepth(cx, cg, offset);
192     }
193 0     return offset;
194 }
195
196 ptrdiff_t
197 js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,
198          jsbytecode op2)
199 1013410 {
200 1013410     ptrdiff_t offset = EmitCheck(cx, cg, op, 3);
201
202 1013410     if (offset >= 0) {
203 1013410         jsbytecode *next = CG_NEXT(cg);
204 1013410         next[0] = (jsbytecode)op;
205 1013410         next[1] = op1;
206 1013410         next[2] = op2;
207 1013410         CG_NEXT(cg) = next + 3;
208 1013410         UpdateDepth(cx, cg, offset);
209     }
210 1013410     return offset;
211 }
212
213 ptrdiff_t
214 js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
215 0 {
216 0     ptrdiff_t length = 1 + (ptrdiff_t)extra;
217 0     ptrdiff_t offset = EmitCheck(cx, cg, op, length);
218
219 0     if (offset >= 0) {
220 0         jsbytecode *next = CG_NEXT(cg);
221 0         *next = (jsbytecode)op;
222 0         memset(next + 1, 0, BYTECODE_SIZE(extra));
223 0         CG_NEXT(cg) = next + length;
224 0         UpdateDepth(cx, cg, offset);
225     }
226 0     return offset;
227 }
228
229 /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
230 const char js_with_statement_str[] = "with statement";
231
232 static const char *statementName[] = {
233     "block",                 /* BLOCK */
234     "label statement",       /* LABEL */
235     "if statement",          /* IF */
236     "else statement",        /* ELSE */
237     "switch statement",      /* SWITCH */
238     js_with_statement_str,   /* WITH */
239     "try statement",         /* TRY */
240     "catch block",           /* CATCH */
241     "finally statement",     /* FINALLY */
242     "do loop",               /* DO_LOOP */
243     "for loop",              /* FOR_LOOP */
244     "for/in loop",           /* FOR_IN_LOOP */
245     "while loop",            /* WHILE_LOOP */
246 };
247
248 static const char *
249 StatementName(JSCodeGenerator *cg)
250 0 {
251 0     if (!cg->treeContext.topStmt)
252 0         return "script";
253 0     return statementName[cg->treeContext.topStmt->type];
254 }
255
256 static void
257 ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)
258 0 {
259 0     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
260                          StatementName(cg));
261 }
262
263 /**
264   Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP)
265   and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided
266   into unconditional (gotos and gosubs), and conditional jumps or branches
267   (which pop a value, test it, and jump depending on its value).  Most jumps
268   have just one immediate operand, a signed offset from the jump opcode's pc
269   to the target bytecode.  The lookup and table switch opcodes may contain
270   many jump offsets.
271
272   Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was
273   fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is
274   suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for
275   the extended form of the JSOP_OR branch opcode).  The unextended or short
276   formats have 16-bit signed immediate offset operands, the extended or long
277   formats have 32-bit signed immediates.  The span-dependency problem consists
278   of selecting as few long instructions as possible, or about as few -- since
279   jumps can span other jumps, extending one jump may cause another to need to
280   be extended.
281
282   Most JS scripts are short, so need no extended jumps.  We optimize for this
283   case by generating short jumps until we know a long jump is needed.  After
284   that point, we keep generating short jumps, but each jump's 16-bit immediate
285   offset operand is actually an unsigned index into cg->spanDeps, an array of
286   JSSpanDep structs.  Each struct tells the top offset in the script of the
287   opcode, the "before" offset of the jump (which will be the same as top for
288   simplex jumps, but which will index further into the bytecode array for a
289   non-initial jump offset in a lookup or table switch), the after "offset"
290   adjusted during span-dependent instruction selection (initially the same
291   value as the "before" offset), and the jump target (more below).
292
293   Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must
294   ensure that all bytecode generated so far can be inspected to discover where
295   the jump offset immediate operands lie within CG_CODE(cg).  But the bonus is
296   that we generate span-dependency records sorted by their offsets, so we can
297   binary-search when trying to find a JSSpanDep for a given bytecode offset,
298   or the nearest JSSpanDep at or above a given pc.
299
300   To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows
301   65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand.  This
302   tells us that we need to binary-search for the cg->spanDeps entry by the
303   jump opcode's bytecode offset (sd->before).
304
305   Jump targets need to be maintained in a data structure that lets us look
306   up an already-known target by its address (jumps may have a common target),
307   and that also lets us update the addresses (script-relative, a.k.a. absolute
308   offsets) of targets that come after a jump target (for when a jump below
309   that target needs to be extended).  We use an AVL tree, implemented using
310   recursion, but with some tricky optimizations to its height-balancing code
311   (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html).
312
313   A final wrinkle: backpatch chains are linked by jump-to-jump offsets with
314   positive sign, even though they link "backward" (i.e., toward lower bytecode
315   address).  We don't want to waste space and search time in the AVL tree for
316   such temporary backpatch deltas, so we use a single-bit wildcard scheme to
317   tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas
318   in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known
319   target, or is still awaiting backpatching.
320
321   Note that backpatch chains would present a problem for BuildSpanDepTable,
322   which inspects bytecode to build cg->spanDeps on demand, when the first
323   short jump offset overflows.  To solve this temporary problem, we emit a
324   proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a
325   result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP
326   for branch ops) whose nuses/ndefs counts help keep the stack balanced, but
327   whose opcode format distinguishes its backpatch delta immediate operand from
328   a normal jump offset.
329  */
330 static int
331 BalanceJumpTargets(JSJumpTarget **jtp)
332 0 {
333 0     JSJumpTarget *jt, *jt2, *root;
334 0     int dir, otherDir, heightChanged;
335 0     JSBool doubleRotate;
336
337 0     jt = *jtp;
338 0     JS_ASSERT(jt->balance != 0);
339
340 0     if (jt->balance < -1) {
341 0         dir = JT_RIGHT;
342 0         doubleRotate = (jt->kids[JT_LEFT]->balance > 0);
343 0     } else if (jt->balance > 1) {
344 0         dir = JT_LEFT;
345 0         doubleRotate = (jt->kids[JT_RIGHT]->balance < 0);
346     } else {
347 0         return 0;
348     }
349
350 0     otherDir = JT_OTHER_DIR(dir);
351 0     if (doubleRotate) {
352 0         jt2 = jt->kids[otherDir];
353 0         *jtp = root = jt2->kids[dir];
354
355 0         jt->kids[otherDir] = root->kids[dir];
356 0         root->kids[dir] = jt;
357
358 0         jt2->kids[dir] = root->kids[otherDir];
359 0         root->kids[otherDir] = jt2;
360
361 0         heightChanged = 1;
362 0         root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0);
363 0         root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0);
364 0         root->balance = 0;
365     } else {
366 0         *jtp = root = jt->kids[otherDir];
367 0         jt->kids[otherDir] = root->kids[dir];
368 0         root->kids[dir] = jt;
369
370 0         heightChanged = (root->balance != 0);
371 0         jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance);
372     }
373
374 0     return heightChanged;
375 }
376
377 typedef struct AddJumpTargetArgs {
378     JSContext           *cx;
379     JSCodeGenerator     *cg;
380     ptrdiff_t           offset;
381     JSJumpTarget        *node;
382 } AddJumpTargetArgs;
383
384 static int
385 AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp)
386 0 {
387 0     JSJumpTarget *jt;
388 0     int balanceDelta;
389
390 0     jt = *jtp;
391 0     if (!jt) {
392 0         JSCodeGenerator *cg = args->cg;
393
394 0         jt = cg->jtFreeList;
395 0         if (jt) {
396 0             cg->jtFreeList = jt->kids[JT_LEFT];
397         } else {
398 0             JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool,
399                                    sizeof *jt);
400 0             if (!jt) {
401 0                 JS_ReportOutOfMemory(args->cx);
402 0                 return 0;
403             }
404         }
405 0         jt->offset = args->offset;
406 0         jt->balance = 0;
407 0         jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL;
408 0         cg->numJumpTargets++;
409 0         args->node = jt;
410 0         *jtp = jt;
411 0         return 1;
412     }
413
414 0     if (jt->offset == args->offset) {
415 0         args->node = jt;
416 0         return 0;
417     }
418
419 0     if (args->offset < jt->offset)
420 0         balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]);
421     else
422 0         balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]);
423 0     if (!args->node)
424 0         return 0;
425
426 0     jt->balance += balanceDelta;
427 0     return (balanceDelta && jt->balance)
428            ? 1 - BalanceJumpTargets(jtp)
429            : 0;
430 }
431
432 #ifdef DEBUG_brendan
433 static int AVLCheck(JSJumpTarget *jt)
434 {
435     int lh, rh;
436
437     if (!jt) return 0;
438     JS_ASSERT(-1 <= jt->balance && jt->balance <= 1);
439     lh = AVLCheck(jt->kids[JT_LEFT]);
440     rh = AVLCheck(jt->kids[JT_RIGHT]);
441     JS_ASSERT(jt->balance == rh - lh);
442     return 1 + JS_MAX(lh, rh);
443 }
444 #endif
445
446 static JSBool
447 SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd,
448                  ptrdiff_t off)
449 0 {
450 0     AddJumpTargetArgs args;
451
452 0     if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) {
453 0         ReportStatementTooLarge(cx, cg);
454 0         return JS_FALSE;
455     }
456
457 0     args.cx = cx;
458 0     args.cg = cg;
459 0     args.offset = sd->top + off;
460 0     args.node = NULL;
461 0     AddJumpTarget(&args, &cg->jumpTargets);
462 0     if (!args.node)
463 0         return JS_FALSE;
464
465 #ifdef DEBUG_brendan
466     AVLCheck(cg->jumpTargets);
467 #endif
468
469 0     SD_SET_TARGET(sd, args.node);
470 0     return JS_TRUE;
471 }
472
473 #define SPANDEPS_MIN            256
474 #define SPANDEPS_SIZE(n)        ((n) * sizeof(JSSpanDep))
475 #define SPANDEPS_SIZE_MIN       SPANDEPS_SIZE(SPANDEPS_MIN)
476
477 static JSBool
478 AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2,
479            ptrdiff_t off)
480 0 {
481 0     uintN index;
482 0     JSSpanDep *sdbase, *sd;
483 0     size_t size;
484
485 0     index = cg->numSpanDeps;
486 0     if (index + 1 == 0) {
487 0         ReportStatementTooLarge(cx, cg);
488 0         return JS_FALSE;
489     }
490
491 0     if ((index & (index - 1)) == 0 &&
492         (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) {
493 0         if (!sdbase) {
494 0             size = SPANDEPS_SIZE_MIN;
495 0             JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size);
496         } else {
497 0             size = SPANDEPS_SIZE(index);
498 0             JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size);
499         }
500 0         if (!sdbase)
501 0             return JS_FALSE;
502 0         cg->spanDeps = sdbase;
503     }
504
505 0     cg->numSpanDeps = index + 1;
506 0     sd = cg->spanDeps + index;
507 0     sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode);
508 0     sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode);
509
510 0     if (js_CodeSpec[*pc].format & JOF_BACKPATCH) {
511         /* Jump offset will be backpatched if off is a non-zero "bpdelta". */
512 0         if (off != 0) {
513 0             JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN);
514 0             if (off > BPDELTA_MAX) {
515 0                 ReportStatementTooLarge(cx, cg);
516 0                 return JS_FALSE;
517             }
518         }
519 0         SD_SET_BPDELTA(sd, off);
520 0     } else if (off == 0) {
521         /* Jump offset will be patched directly, without backpatch chaining. */
522 0         SD_SET_TARGET(sd, NULL);
523     } else {
524         /* The jump offset in off is non-zero, therefore it's already known. */
525 0         if (!SetSpanDepTarget(cx, cg, sd, off))
526 0             return JS_FALSE;
527     }
528
529 0     if (index > SPANDEP_INDEX_MAX)
530 0         index = SPANDEP_INDEX_HUGE;
531 0     SET_SPANDEP_INDEX(pc2, index);
532 0     return JS_TRUE;
533 }
534
535 static JSBool
536 BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg)
537 0 {
538 0     jsbytecode *pc, *end;
539 0     JSOp op;
540 0     const JSCodeSpec *cs;
541 0     ptrdiff_t len, off;
542
543 0     pc = CG_BASE(cg);
544 0     end = CG_NEXT(cg);
545 0     while (pc < end) {
546 0         op = (JSOp)*pc;
547 0         cs = &js_CodeSpec[op];
548 0         len = (ptrdiff_t)cs->length;
549
550 0         switch (cs->format & JOF_TYPEMASK) {
551           case JOF_JUMP:
552 0             off = GET_JUMP_OFFSET(pc);
553 0             if (!AddSpanDep(cx, cg, pc, pc, off))
554 0                 return JS_FALSE;
555 0             break;
556
557 #if JS_HAS_SWITCH_STATEMENT
558           case JOF_TABLESWITCH:
559           {
560 0             jsbytecode *pc2;
561 0             jsint i, low, high;
562
563 0             pc2 = pc;
564 0             off = GET_JUMP_OFFSET(pc2);
565 0             if (!AddSpanDep(cx, cg, pc, pc2, off))
566 0                 return JS_FALSE;
567 0             pc2 += JUMP_OFFSET_LEN;
568 0             low = GET_JUMP_OFFSET(pc2);
569 0             pc2 += JUMP_OFFSET_LEN;
570 0             high = GET_JUMP_OFFSET(pc2);
571 0             pc2 += JUMP_OFFSET_LEN;
572 0             for (i = low; i <= high; i++) {
573 0                 off = GET_JUMP_OFFSET(pc2);
574 0                 if (!AddSpanDep(cx, cg, pc, pc2, off))
575 0                     return JS_FALSE;
576 0                 pc2 += JUMP_OFFSET_LEN;
577             }
578 0             len = 1 + pc2 - pc;
579 0             break;
580           }
581
582           case JOF_LOOKUPSWITCH:
583           {
584 0             jsbytecode *pc2;
585 0             jsint npairs;
586
587 0             pc2 = pc;
588 0             off = GET_JUMP_OFFSET(pc2);
589 0             if (!AddSpanDep(cx, cg, pc, pc2, off))
590 0                 return JS_FALSE;
591 0             pc2 += JUMP_OFFSET_LEN;
592 0             npairs = (jsint) GET_ATOM_INDEX(pc2);
593 0             pc2 += ATOM_INDEX_LEN;
594 0             while (npairs) {
595 0                 pc2 += ATOM_INDEX_LEN;
596 0                 off = GET_JUMP_OFFSET(pc2);
597 0                 if (!AddSpanDep(cx, cg, pc, pc2, off))
598 0                     return JS_FALSE;
599 0                 pc2 += JUMP_OFFSET_LEN;
600 0                 npairs--;
601             }
602 0             len = 1 + pc2 - pc;
603             break;
604           }
605 #endif /* JS_HAS_SWITCH_STATEMENT */
606         }
607
608 0         pc += len;
609     }
610
611 0     return JS_TRUE;
612 }
613
614 static JSSpanDep *
615 GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc)
616 0 {
617 0     uintN index;
618 0     ptrdiff_t offset;
619 0     int lo, hi, mid;
620 0     JSSpanDep *sd;
621
622 0     index = GET_SPANDEP_INDEX(pc);
623 0     if (index != SPANDEP_INDEX_HUGE)
624 0         return cg->spanDeps + index;
625
626 0     offset = PTRDIFF(pc, CG_BASE(cg), jsbytecode);
627 0     lo = 0;
628 0     hi = cg->numSpanDeps - 1;
629 0     while (lo <= hi) {
630 0         mid = (lo + hi) / 2;
631 0         sd = cg->spanDeps + mid;
632 0         if (sd->before == offset)
633 0             return sd;
634 0         if (sd->before < offset)
635 0             lo = mid + 1;
636         else
637 0             hi = mid - 1;
638     }
639
640 0     JS_ASSERT(0);
641 0     return NULL;
642 }
643
644 static JSBool
645 SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
646                   ptrdiff_t delta)
647 819 {
648 819     JSSpanDep *sd;
649
650 819     JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
651 819     if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) {
652 819         SET_JUMP_OFFSET(pc, delta);
653 819         return JS_TRUE;
654     }
655
656 0     if (delta > BPDELTA_MAX) {
657 0         ReportStatementTooLarge(cx, cg);
658 0         return JS_FALSE;
659     }
660
661 0     if (!cg->spanDeps && !BuildSpanDepTable(cx, cg))
662 0         return JS_FALSE;
663
664 0     sd = GetSpanDep(cg, pc);
665 0     JS_ASSERT(SD_GET_BPDELTA(sd) == 0);
666 0     SD_SET_BPDELTA(sd, delta);
667 0     return JS_TRUE;
668 }
669
670 static void
671 UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta)
672 0 {
673 0     if (jt->offset > pivot) {
674 0         jt->offset += delta;
675 0         if (jt->kids[JT_LEFT])
676 0             UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta);
677     }
678 0     if (jt->kids[JT_RIGHT])
679 0         UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta);
680 }
681
682 static JSSpanDep *
683 FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo,
684                    JSSpanDep *guard)
685 0 {
686 0     int num, hi, mid;
687 0     JSSpanDep *sdbase, *sd;
688
689 0     num = cg->numSpanDeps;
690 0     JS_ASSERT(num > 0);
691 0     hi = num - 1;
692 0     sdbase = cg->spanDeps;
693 0     while (lo <= hi) {
694 0         mid = (lo + hi) / 2;
695 0         sd = sdbase + mid;
696 0         if (sd->before == offset)
697 0             return sd;
698 0         if (sd->before < offset)
699 0             lo = mid + 1;
700         else
701 0             hi = mid - 1;
702     }
703 0     if (lo == num)
704 0         return guard;
705 0     sd = sdbase + lo;
706 0     JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset));
707 0     return sd;
708 }
709
710 static void
711 FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt)
712 0 {
713 0     if (jt->kids[JT_LEFT])
714 0         FreeJumpTargets(cg, jt->kids[JT_LEFT]);
715 0     if (jt->kids[JT_RIGHT])
716 0         FreeJumpTargets(cg, jt->kids[JT_RIGHT]);
717 0     jt->kids[JT_LEFT] = cg->jtFreeList;
718 0     cg->jtFreeList = jt;
719 }
720
721 static JSBool
722 OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
723 0 {
724 0     jsbytecode *pc, *oldpc, *base, *limit, *next;
725 0     JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard;
726 0     ptrdiff_t offset, growth, delta, top, pivot, span, length, target;
727 0     JSBool done;
728 0     JSOp op;
729 0     uint32 type;
730 0     size_t size, incr;
731 0     jssrcnote *sn, *snlimit;
732 0     JSSrcNoteSpec *spec;
733 0     uintN i, n, noteIndex;
734 0     JSTryNote *tn, *tnlimit;
735 #ifdef DEBUG_brendan
736     int passes = 0;
737 #endif
738
739 0     base = CG_BASE(cg);
740 0     sdbase = cg->spanDeps;
741 0     sdlimit = sdbase + cg->numSpanDeps;
742 0     offset = CG_OFFSET(cg);
743 0     growth = 0;
744
745 0     do {
746 0         done = JS_TRUE;
747 0         delta = 0;
748 0         top = pivot = -1;
749 0         sdtop = NULL;
750 0         pc = NULL;
751 0         op = JSOP_NOP;
752 0         type = 0;
753 #ifdef DEBUG_brendan
754         passes++;
755 #endif
756
757 0         for (sd = sdbase; sd < sdlimit; sd++) {
758 0             JS_ASSERT(JT_HAS_TAG(sd->target));
759 0             sd->offset += delta;
760
761 0             if (sd->top != top) {
762 0                 sdtop = sd;
763 0                 top = sd->top;
764 0                 JS_ASSERT(top == sd->before);
765 0                 pivot = sd->offset;
766 0                 pc = base + top;
767 0                 op = (JSOp) *pc;
768 0                 type = (js_CodeSpec[op].format & JOF_TYPEMASK);
769 0                 if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
770                     /*
771                      * We already extended all the jump offset operands for
772                      * the opcode at sd->top.  Jumps and branches have only
773                      * one jump offset operand, but switches have many, all
774                      * of which are adjacent in cg->spanDeps.
775                      */
776 0                     continue;
777                 }
778
779 0                 JS_ASSERT(type == JOF_JUMP ||
780                           type == JOF_TABLESWITCH ||
781                           type == JOF_LOOKUPSWITCH);
782             }
783
784 0             if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) {
785 0                 span = SD_TARGET_OFFSET(sd) - pivot;
786 0                 if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) {
787 0                     ptrdiff_t deltaFromTop = 0;
788
789 0                     done = JS_FALSE;
790
791 0                     switch (op) {
792 0                       case JSOP_GOTO:         op = JSOP_GOTOX; break;
793 0                       case JSOP_IFEQ:         op = JSOP_IFEQX; break;
794 0                       case JSOP_IFNE:         op = JSOP_IFNEX; break;
795 0                       case JSOP_OR:           op = JSOP_ORX; break;
796 0                       case JSOP_AND:          op = JSOP_ANDX; break;
797 0                       case JSOP_GOSUB:        op = JSOP_GOSUBX; break;
798 0                       case JSOP_CASE:         op = JSOP_CASEX; break;
799 0                       case JSOP_DEFAULT:      op = JSOP_DEFAULTX; break;
800 0                       case JSOP_TABLESWITCH:  op = JSOP_TABLESWITCHX; break;
801 0                       case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break;
802 0                       default:                JS_ASSERT(0);
803                     }
804 0                     *pc = (jsbytecode) op;
805
806 0                     for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) {
807 0                         if (sd2 <= sd) {
808                             /*
809                              * sd2->offset already includes delta as it stood
810                              * before we entered this loop, but it must also
811                              * include the delta relative to top due to all the
812                              * extended jump offset immediates for the opcode
813                              * starting at top, which we extend in this loop.
814                              *
815                              * If there is only one extended jump offset, then
816                              * sd2->offset won't change and this for loop will
817                              * iterate once only.
818                              */
819 0                             sd2->offset += deltaFromTop;
820 0                             deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN;
821                         } else {
822                             /*
823                              * sd2 comes after sd, and won't be revisited by
824                              * the outer for loop, so we have to increase its
825                              * offset by delta, not merely by deltaFromTop.
826                              */
827 0                             sd2->offset += delta;
828                         }
829
830 0                         delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN;
831 0                         UpdateJumpTargets(cg->jumpTargets, sd2->offset,
832                                           JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN);
833                     }
834 0                     sd = sd2 - 1;
835                 }
836             }
837         }
838
839 0         growth += delta;
840 0     } while (!done);
841
842 0     if (growth) {
843 #ifdef DEBUG_brendan
844         printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n",
845                cg->filename ? cg->filename : "stdin", cg->firstLine,
846                growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps,
847                passes, offset + growth, offset, growth);
848 #endif
849
850         /*
851          * Ensure that we have room for the extended jumps, but don't round up
852          * to a power of two -- we're done generating code, so we cut to fit.
853          */
854 0         limit = CG_LIMIT(cg);
855 0         length = offset + growth;
856 0         next = base + length;
857 0         if (next > limit) {
858 0             JS_ASSERT(length > BYTECODE_CHUNK);
859 0             size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
860 0             incr = BYTECODE_SIZE(length) - size;
861 0             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
862 0             if (!base) {
863 0                 JS_ReportOutOfMemory(cx);
864 0                 return JS_FALSE;
865             }
866 0             CG_BASE(cg) = base;
867 0             CG_LIMIT(cg) = next = base + length;
868         }
869 0         CG_NEXT(cg) = next;
870
871         /*
872          * Set up a fake span dependency record to guard the end of the code
873          * being generated.  This guard record is returned as a fencepost by
874          * FindNearestSpanDep if there is no real spandep at or above a given
875          * unextended code offset.
876          */
877 0         guard.top = -1;
878 0         guard.offset = offset + growth;
879 0         guard.before = offset;
880 0         guard.target = NULL;
881     }
882
883     /*
884      * Now work backwards through the span dependencies, copying chunks of
885      * bytecode between each extended jump toward the end of the grown code
886      * space, and restoring immediate offset operands for all jump bytecodes.
887      * The first chunk of bytecodes, starting at base and ending at the first
888      * extended jump offset (NB: this chunk includes the operation bytecode
889      * just before that immediate jump offset), doesn't need to be copied.
890      */
891 0     JS_ASSERT(sd == sdlimit);
892 0     top = -1;
893 0     while (--sd >= sdbase) {
894 0         if (sd->top != top) {
895 0             top = sd->top;
896 0             op = (JSOp) base[top];
897 0             type = (js_CodeSpec[op].format & JOF_TYPEMASK);
898
899 0             for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--)
900 0                 continue;
901 0             sd2++;
902 0             pivot = sd2->offset;
903 0             JS_ASSERT(top == sd2->before);
904         }
905
906 0         oldpc = base + sd->before;
907 0         span = SD_TARGET_OFFSET(sd) - pivot;
908
909         /*
910          * If this jump didn't need to be extended, restore its span immediate
911          * offset operand now, overwriting the index of sd within cg->spanDeps
912          * that was stored temporarily after *pc when BuildSpanDepTable ran.
913          *
914          * Note that span might fit in 16 bits even for an extended jump op,
915          * if the op has multiple span operands, not all of which overflowed
916          * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in
917          * range for a short jump, but others are not).
918          */
919 0         if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) {
920 0             JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX);
921 0             SET_JUMP_OFFSET(oldpc, span);
922 0             continue;
923         }
924
925         /*
926          * Set up parameters needed to copy the next run of bytecode starting
927          * at offset (which is a cursor into the unextended, original bytecode
928          * vector), down to sd->before (a cursor of the same scale as offset,
929          * it's the index of the original jump pc).  Reuse delta to count the
930          * nominal number of bytes to copy.
931          */
932 0         pc = base + sd->offset;
933 0         delta = offset - sd->before;
934 0         JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
935
936         /*
937          * Don't bother copying the jump offset we're about to reset, but do
938          * copy the bytecode at oldpc (which comes just before its immediate
939          * jump offset operand), on the next iteration through the loop, by
940          * including it in offset's new value.
941          */
942 0         offset = sd->before + 1;
943 0         size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN));
944 0         if (size) {
945 0             memmove(pc + 1 + JUMPX_OFFSET_LEN,
946                     oldpc + 1 + JUMP_OFFSET_LEN,
947                     size);
948         }
949
950 0         SET_JUMPX_OFFSET(pc, span);
951     }
952
953 0     if (growth) {
954         /*
955          * Fix source note deltas.  Don't hardwire the delta fixup adjustment,
956          * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN
957          * at each sd that moved.  The future may bring different offset sizes
958          * for span-dependent instruction operands.  However, we fix only main
959          * notes here, not prolog notes -- we know that prolog opcodes are not
960          * span-dependent, and aren't likely ever to be.
961          */
962 0         offset = growth = 0;
963 0         sd = sdbase;
964 0         for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount;
965              sn < snlimit;
966              sn = SN_NEXT(sn)) {
967             /*
968              * Recall that the offset of a given note includes its delta, and
969              * tells the offset of the annotated bytecode from the main entry
970              * point of the script.
971              */
972 0             offset += SN_DELTA(sn);
973 0             while (sd < sdlimit && sd->before < offset) {
974                 /*
975                  * To compute the delta to add to sn, we need to look at the
976                  * spandep after sd, whose offset - (before + growth) tells by
977                  * how many bytes sd's instruction grew.
978                  */
979 0                 sd2 = sd + 1;
980 0                 if (sd2 == sdlimit)
981 0                     sd2 = &guard;
982 0                 delta = sd2->offset - (sd2->before + growth);
983 0                 if (delta > 0) {
984 0                     JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN);
985 0                     sn = js_AddToSrcNoteDelta(cx, cg, sn, delta);
986 0                     if (!sn)
987 0                         return JS_FALSE;
988 0                     snlimit = cg->main.notes + cg->main.noteCount;
989 0                     growth += delta;
990                 }
991 0                 sd++;
992             }
993
994             /*
995              * If sn has span-dependent offset operands, check whether each
996              * covers further span-dependencies, and increase those operands
997              * accordingly.  Some source notes measure offset not from the
998              * annotated pc, but from that pc plus some small bias.  NB: we
999              * assume that spec->offsetBias can't itself span span-dependent
1000              * instructions!
1001              */
1002 0             spec = &js_SrcNoteSpec[SN_TYPE(sn)];
1003 0             if (spec->isSpanDep) {
1004 0                 pivot = offset + spec->offsetBias;
1005 0                 n = spec->arity;
1006 0                 for (i = 0; i < n; i++) {
1007 0                     span = js_GetSrcNoteOffset(sn, i);
1008 0                     if (span == 0)
1009 0                         continue;
1010 0                     target = pivot + span * spec->isSpanDep;
1011 0                     sd2 = FindNearestSpanDep(cg, target,
1012                                              (target >= pivot)
1013                                              ? sd - sdbase
1014                                              : 0,
1015                                              &guard);
1016
1017                     /*
1018                      * Increase target by sd2's before-vs-after offset delta,
1019                      * which is absolute (i.e., relative to start of script,
1020                      * as is target).  Recompute the span by subtracting its
1021                      * adjusted pivot from target.
1022                      */
1023 0                     target += sd2->offset - sd2->before;
1024 0                     span = target - (pivot + growth);
1025 0                     span *= spec->isSpanDep;
1026 0                     noteIndex = sn - cg->main.notes;
1027 0                     if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span))
1028 0                         return JS_FALSE;
1029 0                     sn = cg->main.notes + noteIndex;
1030 0                     snlimit = cg->main.notes + cg->main.noteCount;
1031                 }
1032             }
1033         }
1034
1035         /*
1036          * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's
1037          * not clear how we can beat that).
1038          */
1039 0         for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) {
1040             /*
1041              * First, look for the nearest span dependency at/above tn->start.
1042              * There may not be any such spandep, in which case the guard will
1043              * be returned.
1044              */
1045 0             offset = tn->start;
1046 0             sd = FindNearestSpanDep(cg, offset, 0, &guard);
1047 0             delta = sd->offset - sd->before;
1048 0             tn->start = offset + delta;
1049
1050             /*
1051              * Next, find the nearest spandep at/above tn->start + tn->length.
1052              * Use its delta minus tn->start's delta to increase tn->length.
1053              */
1054 0             length = tn->length;
1055 0             sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard);
1056 0             if (sd2 != sd)
1057 0                 tn->length = length + sd2->offset - sd2->before - delta;
1058
1059             /*
1060              * Finally, adjust tn->catchStart upward only if it is non-zero,
1061              * and provided there are spandeps below it that grew.
1062              */
1063 0             offset = tn->catchStart;
1064 0             if (offset != 0) {
1065 0                 sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard);
1066 0                 tn->catchStart = offset + sd->offset - sd->before;
1067             }
1068         }
1069     }
1070
1071 #ifdef DEBUG_brendan
1072   {
1073     uintN bigspans = 0;
1074     top = -1;
1075     for (sd = sdbase; sd < sdlimit; sd++) {
1076         offset = sd->offset;
1077
1078         /* NB: sd->top cursors into the original, unextended bytecode vector. */
1079         if (sd->top != top) {
1080             JS_ASSERT(top == -1 ||
1081                       !JOF_TYPE_IS_EXTENDED_JUMP(type) ||
1082                       bigspans != 0);
1083             bigspans = 0;
1084             top = sd->top;
1085             JS_ASSERT(top == sd->before);
1086             op = (JSOp) base[offset];
1087             type = (js_CodeSpec[op].format & JOF_TYPEMASK);
1088             JS_ASSERT(type == JOF_JUMP ||
1089                       type == JOF_JUMPX ||
1090                       type == JOF_TABLESWITCH ||
1091                       type == JOF_TABLESWITCHX ||
1092                       type == JOF_LOOKUPSWITCH ||
1093                       type == JOF_LOOKUPSWITCHX);
1094             pivot = offset;
1095         }
1096
1097         pc = base + offset;
1098         if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
1099             span = GET_JUMPX_OFFSET(pc);
1100             if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) {
1101                 bigspans++;
1102             } else {
1103                 JS_ASSERT(type == JOF_TABLESWITCHX ||
1104                           type == JOF_LOOKUPSWITCHX);
1105             }
1106         } else {
1107             span = GET_JUMP_OFFSET(pc);
1108         }
1109         JS_ASSERT(SD_TARGET_OFFSET(sd) == pivot + span);
1110     }
1111     JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0);
1112   }
1113 #endif
1114
1115     /*
1116      * Reset so we optimize at most once -- cg may be used for further code
1117      * generation of successive, independent, top-level statements.  No jump
1118      * can span top-level statements, because JS lacks goto.
1119      */
1120 0     size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps)));
1121 0     JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps,
1122                            JS_MAX(size, SPANDEPS_SIZE_MIN));
1123 0     cg->spanDeps = NULL;
1124 0     FreeJumpTargets(cg, cg->jumpTargets);
1125 0     cg->jumpTargets = NULL;
1126 0     cg->numSpanDeps = cg->numJumpTargets = 0;
1127 0     return JS_TRUE;
1128 }
1129
1130 static JSBool
1131 EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off)
1132 26883 {
1133 26883     ptrdiff_t jmp;
1134 26883     jsbytecode *pc;
1135
1136 26883     if (off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off) {
1137 0         if (!cg->spanDeps && !BuildSpanDepTable(cx, cg))
1138 0             return JS_FALSE;
1139     }
1140
1141 26883     jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off));
1142 26883     if (jmp >= 0 && cg->spanDeps) {
1143 0         pc = CG_CODE(cg, jmp);
1144 0         if (!AddSpanDep(cx, cg, pc, pc, off))
1145 0             return JS_FALSE;
1146     }
1147 26883     return jmp;
1148 }
1149
1150 static ptrdiff_t
1151 GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc)
1152 9639 {
1153 9639     JSSpanDep *sd;
1154 9639     JSJumpTarget *jt;
1155 9639     ptrdiff_t top;
1156
1157 9639     if (!cg->spanDeps)
1158 9639         return GET_JUMP_OFFSET(pc);
1159
1160 0     sd = GetSpanDep(cg, pc);
1161 0     jt = sd->target;
1162 0     if (!JT_HAS_TAG(jt))
1163 0         return JT_TO_BPDELTA(jt);
1164
1165 0     top = sd->top;
1166 0     while (--sd >= cg->spanDeps && sd->top == top)
1167 0         continue;
1168 0     sd++;
1169 0     return JT_CLR_TAG(jt)->offset - sd->offset;
1170 }
1171
1172 JSBool
1173 js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
1174                  ptrdiff_t off)
1175 25084 {
1176 25084     if (!cg->spanDeps) {
1177 25084         if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) {
1178 25084             SET_JUMP_OFFSET(pc, off);
1179 25084             return JS_TRUE;
1180         }
1181
1182 0         if (!BuildSpanDepTable(cx, cg))
1183 0             return JS_FALSE;
1184     }
1185
1186 0     return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off);
1187 }
1188
1189 JSBool
1190 js_InWithStatement(JSTreeContext *tc)
1191 114546 {
1192 114546     JSStmtInfo *stmt;
1193
1194 471954     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1195 357408         if (stmt->type == STMT_WITH)
1196 0             return JS_TRUE;
1197     }
1198 114546     return JS_FALSE;
1199 }
1200
1201 JSBool
1202 js_InCatchBlock(JSTreeContext *tc, JSAtom *atom)
1203 110296 {
1204 110296     JSStmtInfo *stmt;
1205
1206 462808     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1207 352512         if (stmt->type == STMT_CATCH && stmt->label == atom)
1208 0             return JS_TRUE;
1209     }
1210 110296     return JS_FALSE;
1211 }
1212
1213 void
1214 js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
1215                  ptrdiff_t top)
1216 69470 {
1217 69470     stmt->type = type;
1218 69470     SET_STATEMENT_TOP(stmt, top);
1219 69470     stmt->label = NULL;
1220 69470     stmt->down = tc->topStmt;
1221 69470     tc->topStmt = stmt;
1222 }
1223
1224 /*
1225  * Emit a backpatch op with offset pointing to the previous jump of this type,
1226  * so that we can walk back up the chain fixing up the op and jump offset.
1227  */
1228 #define EMIT_BACKPATCH_OP(cx, cg, last, op, jmp)                              \
1229     JS_BEGIN_MACRO                                                            \
1230         ptrdiff_t offset, delta;                                              \
1231         offset = CG_OFFSET(cg);                                               \
1232         delta = offset - (last);                                              \
1233         last = offset;                                                        \
1234         JS_ASSERT(delta > 0);                                                 \
1235         jmp = EmitJump((cx), (cg), (op), (delta));                            \
1236     JS_END_MACRO
1237
1238 /* Emit additional bytecode(s) for non-local jumps. */
1239 static JSBool
1240 EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
1241                       JSOp *returnop)
1242 8420 {
1243 8420     intN depth;
1244 8420     JSStmtInfo *stmt;
1245 8420     ptrdiff_t jmp;
1246
1247     /*
1248      * Return from within a try block that has a finally clause must be split
1249      * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval;
1250      * and JSOP_RETRVAL, which makes control flow go back to the caller, who
1251      * picks up fp->rval as usual.  Otherwise, the stack will be unbalanced
1252      * when executing the finally clause.
1253      *
1254      * We mutate *returnop once only if we find an enclosing try-block (viz,
1255      * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one
1256      * or more JSOP_GOSUBs and other fixup opcodes emitted by this function.
1257      * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop.
1258      * The fixup opcodes and gosubs must interleave in the proper order, from
1259      * inner statement to outer, so that finally clauses run at the correct
1260      * stack depth.
1261      */
1262 8420     if (returnop) {
1263 2482         JS_ASSERT(*returnop == JSOP_RETURN);
1264 8840         for (stmt = cg->treeContext.topStmt; stmt != toStmt;
1265              stmt = stmt->down) {
1266 6358             if (stmt->type == STMT_FINALLY) {
1267 0                 if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
1268 0                     return JS_FALSE;
1269 0                 *returnop = JSOP_RETRVAL;
1270 0                 break;
1271             }
1272         }
1273
1274         /*
1275          * If there are no try-with-finally blocks open around this return
1276          * statement, we can generate a return forthwith and skip generating
1277          * any fixup code.
1278          */
1279 2482         if (*returnop == JSOP_RETURN)
1280 2482             return JS_TRUE;
1281     }
1282
1283     /*
1284      * The non-local jump fixup we emit will unbalance cg->stackDepth, because
1285      * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
1286      * end of a with statement, so we save cg->stackDepth here and restore it
1287      * just before a successful return.
1288      */
1289 5938     depth = cg->stackDepth;
1290 7485     for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
1291 1547         switch (stmt->type) {
1292           case STMT_FINALLY:
1293 0             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1294 0                 return JS_FALSE;
1295 0             EMIT_BACKPATCH_OP(cx, cg, stmt->gosub, JSOP_BACKPATCH_PUSH, jmp);
1296 0             if (jmp < 0)
1297 0                 return JS_FALSE;
1298 0             break;
1299
1300           case STMT_WITH:
1301           case STMT_CATCH:
1302             /* There's a With object on the stack that we need to pop. */
1303 0             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1304 0                 return JS_FALSE;
1305 0             if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
1306 0                 return JS_FALSE;
1307 0             break;
1308
1309           case STMT_FOR_IN_LOOP:
1310             /*
1311              * The iterator and the object being iterated need to be popped.
1312              * JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN.
1313              */
1314 0             if (js_Emit1(cx, cg, JSOP_POP2) < 0)
1315 0                 return JS_FALSE;
1316 0             break;
1317
1318           case STMT_SUBROUTINE:
1319             /* There's a retsub pc-offset on the stack that we need to pop. */
1320 0             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
1321 0                 return JS_FALSE;
1322 0             if (js_Emit1(cx, cg, JSOP_POP) < 0)
1323 0                 return JS_FALSE;
1324 1547             break;
1325
1326           default:;
1327         }
1328     }
1329
1330 5938     cg->stackDepth = depth;
1331 5938     return JS_TRUE;
1332 }
1333
1334 static ptrdiff_t
1335 EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
1336          ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType)
1337 5938 {
1338 5938     intN index;
1339 5938     ptrdiff_t jmp;
1340
1341 5938     if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
1342 0         return -1;
1343
1344 5938     if (label) {
1345 0         index = js_NewSrcNote(cx, cg, noteType);
1346 0         if (index < 0)
1347 0             return -1;
1348 0         if (!js_SetSrcNoteOffset(cx, cg, (uintN)index, 0,
1349                                  (ptrdiff_t) ALE_INDEX(label))) {
1350 0             return -1;
1351         }
1352 5938     } else if (noteType != SRC_NULL) {
1353 493         if (js_NewSrcNote(cx, cg, noteType) < 0)
1354 0             return -1;
1355     }
1356
1357 5938     EMIT_BACKPATCH_OP(cx, cg, *last, JSOP_BACKPATCH, jmp);
1358 5938     return jmp;
1359 }
1360
1361 static JSBool
1362 BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
1363           jsbytecode *target, jsbytecode op)
1364 70874 {
1365 70874     jsbytecode *pc, *stop;
1366 70874     ptrdiff_t delta, span;
1367
1368 70874     pc = CG_CODE(cg, last);
1369 70874     stop = CG_CODE(cg, -1);
1370 76812     while (pc != stop) {
1371 5938         delta = GetJumpOffset(cg, pc);
1372 5938         span = PTRDIFF(target, pc, jsbytecode);
1373 5938         CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span);
1374
1375         /*
1376          * Set *pc after jump offset in case bpdelta didn't overflow, but span
1377          * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable
1378          * and need to see the JSOP_BACKPATCH* op at *pc).
1379          */
1380 5938         *pc = op;
1381 5938         pc -= delta;
1382     }
1383 70874     return JS_TRUE;
1384 }
1385
1386 void
1387 js_PopStatement(JSTreeContext *tc)
1388 69470 {
1389 69470     tc->topStmt = tc->topStmt->down;
1390 }
1391
1392 JSBool
1393 js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
1394 35437 {
1395 35437     JSStmtInfo *stmt;
1396
1397 35437     stmt = cg->treeContext.topStmt;
1398 35437     if (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
1399         !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update),
1400                    JSOP_GOTO)) {
1401 0         return JS_FALSE;
1402     }
1403 35437     js_PopStatement(&cg->treeContext);
1404 35437     return JS_TRUE;
1405 }
1406
1407 JSBool
1408 js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
1409                              JSParseNode *pn)
1410 8467 {
1411 8467     jsdouble dval;
1412 8467     jsint ival;
1413 8467     JSAtom *valueAtom;
1414 8467     JSAtomListElement *ale;
1415
1416     /* XXX just do numbers for now */
1417 8467     if (pn->pn_type == TOK_NUMBER) {
1418 255         dval = pn->pn_dval;
1419 255         valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival))
1420                     ? js_AtomizeInt(cx, ival, 0)
1421                     : js_AtomizeDouble(cx, dval, 0);
1422 255         if (!valueAtom)
1423 0             return JS_FALSE;
1424 255         ale = js_IndexAtom(cx, atom, &cg->constList);
1425 255         if (!ale)
1426 0             return JS_FALSE;
1427 255         ALE_SET_VALUE(ale, ATOM_KEY(valueAtom));
1428     }
1429 8467     return JS_TRUE;
1430 }
1431
1432 JSBool
1433 js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
1434                              jsval *vp)
1435 0 {
1436 0     JSStackFrame *fp;
1437 0     JSAtomListElement *ale;
1438 0     JSObject *obj, *pobj;
1439 0     JSProperty *prop;
1440 0     uintN attrs;
1441
1442     /*
1443      * fp chases cg down the stack, but only until we reach the outermost cg.
1444      * All stack frames should be flagged with JSFRAME_COMPILING, so we check
1445      * sanity here.
1446      */
1447 0     fp = cx->fp;
1448 0     do {
1449 0         JS_ASSERT(fp->flags & JSFRAME_COMPILING);
1450
1451 0         obj = fp->varobj;
1452 0         if (obj == fp->scopeChain &&
1453             !js_InWithStatement(&cg->treeContext) &&
1454             !js_InCatchBlock(&cg->treeContext, atom)) {
1455 0             ATOM_LIST_SEARCH(ale, &cg->constList, atom);
1456 0             if (ale) {
1457 0                 *vp = ALE_VALUE(ale);
1458 0                 return JS_TRUE;
1459             }
1460
1461 0             if (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO)) {
1462                 /*
1463                  * We're compiling code that will be executed immediately,
1464                  * and not reexecuted against a different scope chain and/or
1465                  * variable object.  Therefore we can get const values from
1466                  * our variable object here.
1467                  *
1468                  * Try looking in the variable object for a direct property
1469                  * that is readonly and permanent.  We know such a property
1470                  * can't be shadowed by another property on obj's prototype
1471                  * chain, or a with object or catch variable; nor can prop's
1472                  * value be changed, nor can prop be deleted.
1473                  */
1474 0                 if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))
1475 0                     return JS_FALSE;
1476 0                 if (pobj == obj) {
1477 0                     if (!OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs))
1478 0                         return JS_FALSE;
1479 0                     if ((~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)) == 0)
1480 0                         return OBJ_GET_PROPERTY(cx, obj, (jsid)atom, vp);
1481                 }
1482             }
1483         }
1484 0         fp = fp->down;
1485 0     } while ((cg = cg->parent) != NULL);
1486 0     return JS_FALSE;
1487 }
1488
1489 /*
1490  * Emit a bytecode and its 2-byte constant (atom) index immediate operand.
1491  * NB: We use cx and cg from our caller's lexical environment, and return
1492  * false on error.
1493  */
1494 #define EMIT_ATOM_INDEX_OP(op, atomIndex)                                     \
1495     JS_BEGIN_MACRO                                                            \
1496         if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex),                    \
1497                                  ATOM_INDEX_LO(atomIndex)) < 0) {             \
1498             return JS_FALSE;                                                  \
1499         }                                                                     \
1500     JS_END_MACRO
1501
1502 static JSBool
1503 EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
1504 577468 {
1505 577468     JSAtomListElement *ale;
1506
1507 577468     ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
1508 577468     if (!ale)
1509 0         return JS_FALSE;
1510 577468     EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));
1511 577468     return JS_TRUE;
1512 }
1513
1514 /*
1515  * This routine tries to optimize name gets and sets to stack slot loads and
1516  * stores, given the variables object and scope chain in cx's top frame, the
1517  * compile-time context in tc, and a TOK_NAME node pn.  It returns false on
1518  * error, true on success.
1519  *
1520  * The caller can inspect pn->pn_slot for a non-negative slot number to tell
1521  * whether optimization occurred, in which case LookupArgOrVar also updated
1522  * pn->pn_op.  If pn->pn_slot is still -1 on return, pn->pn_op nevertheless
1523  * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS.  Whether
1524  * or not pn->pn_op was modified, if this function finds an argument or local
1525  * variable name, pn->pn_attrs will contain the property's attributes after a
1526  * successful return.
1527  */
1528 static JSBool
1529 LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
1530 528112 {
1531 528112     JSObject *obj, *pobj;
1532 528112     JSClass *clasp;
1533 528112     JSAtom *atom;
1534 528112     JSScopeProperty *sprop;
1535 528112     JSOp op;
1536
1537 528112     JS_ASSERT(pn->pn_type == TOK_NAME);
1538 528112     if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
1539 14416         return JS_TRUE;
1540
1541     /*
1542      * We can't optimize if var and closure (a local function not in a larger
1543      * expression and not at top-level within another's body) collide.
1544      * XXX suboptimal: keep track of colliding names and deoptimize only those
1545      */
1546 513696     if (tc->flags & TCF_FUN_CLOSURE_VS_VAR)
1547 0         return JS_TRUE;
1548
1549     /*
1550      * We can't optimize if we're not compiling a function body, whether via
1551      * eval, or directly when compiling a function statement or expression.
1552      */
1553 513696     obj = cx->fp->varobj;
1554 513696     clasp = OBJ_GET_CLASS(cx, obj);
1555 513696     if (clasp != &js_FunctionClass && clasp != &js_CallClass)
1556 403400         return JS_TRUE;
1557
1558     /*
1559      * We can't optimize if we're in an eval called inside a with statement,
1560      * or we're compiling a with statement and its body, or we're in a catch
1561      * block whose exception variable has the same name as pn.
1562      */
1563 110296     atom = pn->pn_atom;
1564 110296     if (cx->fp->scopeChain != obj ||
1565         js_InWithStatement(tc) ||
1566         js_InCatchBlock(tc, atom)) {
1567 0         return JS_TRUE;
1568     }
1569
1570     /*
1571      * Ok, we may be able to optimize name to stack slot. Look for an argument
1572      * or variable property in the function, or its call object, not found in
1573      * any prototype object.  Rewrite pn_op and update pn accordingly.  NB: We
1574      * know that JSOP_DELNAME on an argument or variable must evaluate to
1575      * false, due to JSPROP_PERMANENT.
1576      */
1577 110296     if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop))
1578 0         return JS_FALSE;
1579 110296     op = pn->pn_op;
1580 110296     if (sprop) {
1581 57800         if (pobj == obj) {
1582 57800             JSPropertyOp getter = sprop->getter;
1583
1584 57800             if (getter == js_GetArgument) {
1585 32606                 switch (op) {
1586 32504                   case JSOP_NAME:     op = JSOP_GETARG; break;
1587 102                   case JSOP_SETNAME:  op = JSOP_SETARG; break;
1588 0                   case JSOP_INCNAME:  op = JSOP_INCARG; break;
1589 0                   case JSOP_NAMEINC:  op = JSOP_ARGINC; break;
1590 0                   case JSOP_DECNAME:  op = JSOP_DECARG; break;
1591 0                   case JSOP_NAMEDEC:  op = JSOP_ARGDEC; break;
1592 0                   case JSOP_FORNAME:  op = JSOP_FORARG; break;
1593 0                   case JSOP_DELNAME:  op = JSOP_FALSE; break;
1594 25194                   default: JS_ASSERT(0);
1595                 }
1596 25194             } else if (getter == js_GetLocalVariable ||
1597                        getter == js_GetCallVariable)
1598             {
1599 25194                 switch (op) {
1600 17476                   case JSOP_NAME:     op = JSOP_GETVAR; break;
1601 6800                   case JSOP_SETNAME:  op = JSOP_SETVAR; break;
1602 0                   case JSOP_SETCONST: op = JSOP_SETVAR; break;
1603 102                   case JSOP_INCNAME:  op = JSOP_INCVAR; break;
1604 34                   case JSOP_NAMEINC:  op = JSOP_VARINC; break;
1605 0                   case JSOP_DECNAME:  op = JSOP_DECVAR; break;
1606 0                   case JSOP_NAMEDEC:  op = JSOP_VARDEC; break;
1607 782                   case JSOP_FORNAME:  op = JSOP_FORVAR; break;
1608 0                   case JSOP_DELNAME:  op = JSOP_FALSE; break;
1609 57800                   default: JS_ASSERT(0);
1610                 }
1611             }
1612 57800             if (op != pn->pn_op) {
1613 57800                 pn->pn_op = op;
1614 57800                 pn->pn_slot = sprop->shortid;
1615             }
1616 57800             pn->pn_attrs = sprop->attrs;
1617         }
1618 57800         OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
1619     }
1620
1621 110296     if (pn->pn_slot < 0) {
1622         /*
1623          * We couldn't optimize it, so it's not an arg or local var name.  Now
1624          * we must check for the predefined arguments variable.  It may be
1625          * overridden by assignment, in which case the function is heavyweight
1626          * and the interpreter will look up 'arguments' in the function's call
1627          * object.
1628          */
1629 52496         if (pn->pn_op == JSOP_NAME &&
1630             atom == cx->runtime->atomState.argumentsAtom) {
1631 0             pn->pn_op = JSOP_ARGUMENTS;
1632 0             return JS_TRUE;
1633         }
1634
1635 52496         tc->flags |= TCF_FUN_USES_NONLOCALS;
1636     }
1637 110296     return JS_TRUE;
1638 }
1639
1640 /*
1641  * If pn contains a useful expression, return true with *answer set to true.
1642  * If pn contains a useless expression, return true with *answer set to false.
1643  * Return false on error.
1644  *
1645  * The caller should initialize *answer to false and invoke this function on
1646  * an expression statement or similar subtree to decide whether the tree could
1647  * produce code that has any side effects.  For an expression statement, we
1648  * define useless code as code with no side effects, because the main effect,
1649  * the value left on the stack after the code executes, will be discarded by a
1650  * pop bytecode.
1651  */
1652 static JSBool
1653 CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
1654                  JSBool *answer)
1655 41480 {
1656 41480     JSBool ok;
1657 41480     JSFunction *fun;
1658 41480     JSParseNode *pn2;
1659
1660 41480     ok = JS_TRUE;
1661 41480     if (!pn || *answer)
1662 0         return ok;
1663
1664 41480     switch (pn->pn_arity) {
1665       case PN_FUNC:
1666         /*
1667          * A named function is presumed useful: we can't yet know that it is
1668          * not called.  The side effects are the creation of a scope object
1669          * to parent this function object, and the binding of the function's
1670          * name in that scope object.  See comments at case JSOP_NAMEDFUNOBJ:
1671          * in jsinterp.c.
1672          */
1673 0         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
1674 0         if (fun->atom)
1675 0             *answer = JS_TRUE;
1676 0         break;
1677
1678       case PN_LIST:
1679 35632         if (pn->pn_type == TOK_NEW ||
1680             pn->pn_type == TOK_LP ||
1681             pn->pn_type == TOK_LB) {
1682             /*
1683              * All invocation operations (construct: TOK_NEW, call: TOK_LP)
1684              * are presumed to be useful, because they may have side effects
1685              * even if their main effect (their return value) is discarded.
1686              *
1687              * TOK_LB binary trees of 3 or more nodes are flattened into lists
1688              * to avoid too much recursion.  All such lists must be presumed
1689              * to be useful because each index operation could invoke a getter
1690              * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case,
1691              * does not apply here: arguments[i][j] might invoke a getter).
1692              */
1693 35632             *answer = JS_TRUE;
1694         } else {
1695 0             for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
1696 0                 ok &= CheckSideEffects(cx, tc, pn2, answer);
1697         }
1698 0         break;
1699
1700       case PN_TERNARY:
1701 0         ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) &&
1702              CheckSideEffects(cx, tc, pn->pn_kid2, answer) &&
1703              CheckSideEffects(cx, tc, pn->pn_kid3, answer);
1704 0         break;
1705
1706       case PN_BINARY:
1707 5474         if (pn->pn_type == TOK_ASSIGN) {
1708             /*
1709              * Assignment is presumed to be useful, even if the next operation
1710              * is another assignment overwriting this one's ostensible effect,
1711              * because the left operand may be a property with a setter that
1712              * has side effects.
1713              */
1714 5474             *answer = JS_TRUE;
1715         } else {
1716 0             if (pn->pn_type == TOK_LB) {
1717 0                 pn2 = pn->pn_left;
1718 0                 if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2))
1719 0                     return JS_FALSE;
1720 0                 if (pn2->pn_op != JSOP_ARGUMENTS) {
1721                     /*
1722                      * Any indexed property reference could call a getter with
1723                      * side effects, except for arguments[i] where arguments is
1724                      * unambiguous.
1725                      */
1726 0                     *answer = JS_TRUE;
1727                 }
1728             }
1729 0             ok = CheckSideEffects(cx, tc, pn->pn_left, answer) &&
1730                  CheckSideEffects(cx, tc, pn->pn_right, answer);
1731         }
1732 0         break;
1733
1734       case PN_UNARY:
1735 374         if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC ||
1736             pn->pn_type == TOK_DELETE ||
1737             pn->pn_type == TOK_THROW ||
1738             pn->pn_type == TOK_DEFSHARP) {
1739             /* All these operations have effects that we must commit. */
1740 374             *answer = JS_TRUE;
1741         } else {
1742 0             ok = CheckSideEffects(cx, tc, pn->pn_kid, answer);
1743         }
1744 0         break;
1745
1746       case PN_NAME:
1747 0         if (pn->pn_type == TOK_NAME) {
1748 0             if (!LookupArgOrVar(cx, tc, pn))
1749 0                 return JS_FALSE;
1750 0             if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) {
1751                 /*
1752                  * Not an argument or local variable use, so this expression
1753                  * could invoke a getter that has side effects.
1754                  */
1755 0                 *answer = JS_TRUE;
1756             }
1757         }
1758 0         pn2 = pn->pn_expr;
1759 0         if (pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) {
1760 0             if (!LookupArgOrVar(cx, tc, pn2))
1761 0                 return JS_FALSE;
1762 0             if (!(pn2->pn_op == JSOP_ARGUMENTS &&
1763                   pn->pn_atom == cx->runtime->atomState.lengthAtom)) {
1764                 /*
1765                  * Any dotted property reference could call a getter, except
1766                  * for arguments.length where arguments is unambiguous.
1767                  */
1768 0                 *answer = JS_TRUE;
1769             }
1770         }
1771 0         ok = CheckSideEffects(cx, tc, pn2, answer);
1772 0         break;
1773
1774       case PN_NULLARY:
1775 0         if (pn->pn_type == TOK_DEBUGGER)
1776 0             *answer = JS_TRUE;
1777         break;
1778     }
1779 41480     return ok;
1780 }
1781
1782 static JSBool
1783 EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
1784 29623 {
1785 29623     JSParseNode *pn2, *pndot, *pnup, *pndown;
1786 29623     ptrdiff_t top;
1787 29623     JSAtomListElement *ale;
1788
1789 29623     pn2 = pn->pn_expr;
1790 29623     if (op == JSOP_GETPROP &&
1791         pn->pn_type == TOK_DOT &&
1792         pn2->pn_type == TOK_NAME) {
1793         /* Try to optimize arguments.length into JSOP_ARGCNT. */
1794 20291         if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
1795 0             return JS_FALSE;
1796 20291         if (pn2->pn_op == JSOP_ARGUMENTS &&
1797             pn->pn_atom == cx->runtime->atomState.lengthAtom) {
1798 0             return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0;
1799         }
1800     }
1801
1802     /*
1803      * If the object operand is also a dotted property reference, reverse the
1804      * list linked via pn_expr temporarily so we can iterate over it from the
1805      * bottom up (reversing again as we go), to avoid excessive recursion.
1806      */
1807 29623     if (pn2->pn_type == TOK_DOT) {
1808 5584         pndot = pn2;
1809 5584         pnup = NULL;
1810 5584         top = CG_OFFSET(cg);
1811 5756         for (;;) {
1812             /* Reverse pndot->pn_expr to point up, not down. */
1813 5670             pndot->pn_offset = top;
1814 5670             pndown = pndot->pn_expr;
1815 5670             pndot->pn_expr = pnup;
1816 5670             if (pndown->pn_type != TOK_DOT)
1817 5584                 break;
1818 86             pnup = pndot;
1819 86             pndot = pndown;
1820         }
1821
1822         /* pndown is a primary expression, not a dotted property reference. */
1823 5584         if (!js_EmitTree(cx, cg, pndown))
1824 0             return JS_FALSE;
1825
1826 5670         do {
1827             /* Walk back up the list, emitting annotated name ops. */
1828 5670             if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
1829                                CG_OFFSET(cg) - pndown->pn_offset) < 0) {
1830 0                 return JS_FALSE;
1831             }
1832 5670             ale = js_IndexAtom(cx, pndot->pn_atom, &cg->atomList);
1833 5670             if (!ale)
1834 0                 return JS_FALSE;
1835 5670             EMIT_ATOM_INDEX_OP(pndot->pn_op, ALE_INDEX(ale));
1836
1837             /* Reverse the pn_expr link again. */
1838 5670             pnup = pndot->pn_expr;
1839 5670             pndot->pn_expr = pndown;
1840 5670             pndown = pndot;
1841 5670         } while ((pndot = pnup) != NULL);
1842     } else {
1843 24039         if (!js_EmitTree(cx, cg, pn2))
1844 0             return JS_FALSE;
1845     }
1846
1847 29623     if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0)
1848 0         return JS_FALSE;
1849 29623     if (!pn->pn_atom) {
1850 0         JS_ASSERT(op == JSOP_IMPORTALL);
1851 0         if (js_Emit1(cx, cg, op) < 0)
1852 0             return JS_FALSE;
1853     } else {
1854 29623         ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
1855 29623         if (!ale)
1856 0             return JS_FALSE;
1857 29623         EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));
1858     }
1859 29623     return JS_TRUE;
1860 }
1861
1862 static JSBool
1863 EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
1864 18793 {
1865 18793     ptrdiff_t top;
1866 18793     JSParseNode *left, *right, *next;
1867 18793     jsint slot;
1868
1869 18793     top = CG_OFFSET(cg);
1870 18793     if (pn->pn_arity == PN_LIST) {
1871         /* Left-associative operator chain to avoid too much recursion. */
1872 0         JS_ASSERT(pn->pn_op == JSOP_GETELEM);
1873 0         JS_ASSERT(pn->pn_count >= 3);
1874 0         left = pn->pn_head;
1875 0         right = PN_LAST(pn);
1876 0         next = left->pn_next;
1877 0         JS_ASSERT(next != right);
1878
1879         /*
1880          * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by
1881          * one or more index expression and JSOP_GETELEM op pairs.
1882          */
1883 0         if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
1884 0             if (!LookupArgOrVar(cx, &cg->treeContext, left))
1885 0                 return JS_FALSE;
1886 0             if (left->pn_op == JSOP_ARGUMENTS &&
1887 0                 JSDOUBLE_IS_INT(next->pn_dval, slot) &&
1888                 (jsuint)slot < ATOM_INDEX_LIMIT) {
1889 0                 left->pn_offset = next->pn_offset = top;
1890 0                 EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot);
1891 0                 left = next;
1892 0                 next = left->pn_next;
1893             }
1894         }
1895
1896         /*
1897          * Check whether we generated JSOP_ARGSUB, just above, and have only
1898          * one more index expression to emit.  Given arguments[0][j], we must
1899          * skip the while loop altogether, falling through to emit code for j
1900          * (in the subtree referenced by right), followed by the annotated op,
1901          * at the bottom of this function.
1902          */
1903 0         JS_ASSERT(next != right || pn->pn_count == 3);
1904 0         if (left == pn->pn_head) {
1905 0             if (!js_EmitTree(cx, cg, left))
1906 0                 return JS_FALSE;
1907         }
1908 0         while (next != right) {
1909 0             if (!js_EmitTree(cx, cg, next))
1910 0                 return JS_FALSE;
1911 0             if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
1912 0                 return JS_FALSE;
1913 0             if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
1914 0                 return JS_FALSE;
1915 0             next = next->pn_next;
1916         }
1917     } else {
1918 18793         JS_ASSERT(pn->pn_arity == PN_BINARY);
1919 18793         left = pn->pn_left;
1920 18793         right = pn->pn_right;
1921
1922         /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */
1923 18793         if (op == JSOP_GETELEM &&
1924             left->pn_type == TOK_NAME &&
1925             right->pn_type == TOK_NUMBER) {
1926 8442             if (!LookupArgOrVar(cx, &cg->treeContext, left))
1927 0                 return JS_FALSE;
1928 8442             if (left->pn_op == JSOP_ARGUMENTS &&
1929 0                 JSDOUBLE_IS_INT(right->pn_dval, slot) &&
1930                 (jsuint)slot < ATOM_INDEX_LIMIT) {
1931 0                 left->pn_offset = right->pn_offset = top;
1932 0                 EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot);
1933 0                 return JS_TRUE;
1934             }
1935         }
1936
1937 18793         if (!js_EmitTree(cx, cg, left))
1938 0             return JS_FALSE;
1939     }
1940 18793     if (!js_EmitTree(cx, cg, right))
1941 0         return JS_FALSE;
1942 18793     if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
1943 0         return JS_FALSE;
1944 18793     return js_Emit1(cx, cg, op) >= 0;
1945 }
1946
1947 static JSBool
1948 EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
1949 16349 {
1950 16349     jsint ival;
1951 16349     jsatomid atomIndex;
1952 16349     JSAtom *atom;
1953 16349     JSAtomListElement *ale;
1954
1955 16349     if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
1956 16349         if (ival == 0)
1957 6971             return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
1958 9378         if (ival == 1)
1959 4633             return js_Emit1(cx, cg, JSOP_ONE) >= 0;
1960 4745         if ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) {
1961 4269             atomIndex = (jsatomid)ival;
1962 4269             EMIT_ATOM_INDEX_OP(JSOP_UINT16, atomIndex);
1963 4269             return JS_TRUE;
1964         }
1965 476         atom = js_AtomizeInt(cx, ival, 0);
1966     } else {
1967 0         atom = js_AtomizeDouble(cx, dval, 0);
1968     }
1969 476     if (!atom)
1970 0         return JS_FALSE;
1971 476     ale = js_IndexAtom(cx, atom, &cg->atomList);
1972 476     if (!ale)
1973 0         return JS_FALSE;
1974 476     EMIT_ATOM_INDEX_OP(JSOP_NUMBER, ALE_INDEX(ale));
1975 476     return JS_TRUE;
1976 }
1977
1978 #if JS_HAS_SWITCH_STATEMENT
1979 static JSBool
1980 EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
1981            JSStmtInfo *stmtInfo)
1982 0 {
1983 0     JSOp switchOp;
1984 0     JSBool ok, hasDefault, constPropagated;
1985 0     ptrdiff_t top, off, defaultOffset;
1986 0     JSParseNode *pn2, *pn3, *pn4;
1987 0     uint32 caseCount, tableLength;
1988 0     JSParseNode **table;
1989 0     jsdouble d;
1990 0     jsint i, low, high;
1991 0     JSAtom *atom;
1992 0     JSAtomListElement *ale;
1993 0     intN noteIndex;
1994 0     size_t switchSize, tableSize;
1995 0     jsbytecode *pc, *savepc;
1996
1997     /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
1998 0     switchOp = JSOP_TABLESWITCH;
1999 0     ok = JS_TRUE;
2000 0     hasDefault = constPropagated = JS_FALSE;
2001 0     defaultOffset = -1;
2002
2003     /* Emit code for the discriminant first. */
2004 0     if (!js_EmitTree(cx, cg, pn->pn_kid1))
2005 0         return JS_FALSE;
2006
2007     /* Switch bytecodes run from here till end of final case. */
2008 0     top = CG_OFFSET(cg);
2009 0     js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
2010
2011 0     pn2 = pn->pn_kid2;
2012 0     caseCount = pn2->pn_count;
2013 0     tableLength = 0;
2014 0     table = NULL;
2015
2016 0     if (caseCount == 0 ||
2017         (caseCount == 1 &&
2018          (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) {
2019 0         caseCount = 0;
2020 0         low = 0;
2021 0         high = -1;
2022     } else {
2023 #define INTMAP_LENGTH   256
2024 0         jsbitmap intmap_space[INTMAP_LENGTH];
2025 0         jsbitmap *intmap = NULL;
2026 0         int32 intmap_bitlen = 0;
2027
2028 0         low  = JSVAL_INT_MAX;
2029 0         high = JSVAL_INT_MIN;
2030
2031 0         for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2032 0             if (pn3->pn_type == TOK_DEFAULT) {
2033 0                 hasDefault = JS_TRUE;
2034 0                 caseCount--;    /* one of the "cases" was the default */
2035 0                 continue;
2036             }
2037
2038 0             JS_ASSERT(pn3->pn_type == TOK_CASE);
2039 0             if (switchOp == JSOP_CONDSWITCH)
2040 0                 continue;
2041
2042 0             pn4 = pn3->pn_left;
2043 0             switch (pn4->pn_type) {
2044               case TOK_NUMBER:
2045 0                 d = pn4->pn_dval;
2046 0                 if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
2047 0                     pn3->pn_val = INT_TO_JSVAL(i);
2048                 } else {
2049 0                     atom = js_AtomizeDouble(cx, d, 0);
2050 0                     if (!atom) {
2051 0                         ok = JS_FALSE;
2052 0                         goto release;
2053                     }
2054 0                     pn3->pn_val = ATOM_KEY(atom);
2055                 }
2056 0                 break;
2057               case TOK_STRING:
2058 0                 pn3->pn_val = ATOM_KEY(pn4->pn_atom);
2059 0                 break;
2060               case TOK_NAME:
2061 0                 if (!pn4->pn_expr &&
2062                     js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom,
2063                                                  &pn3->pn_val)) {
2064 0                     constPropagated = JS_TRUE;
2065 0                     break;
2066                 }
2067                 /* FALL THROUGH */
2068               case TOK_PRIMARY:
2069 0                 if (pn4->pn_op == JSOP_TRUE) {
2070 0                     pn3->pn_val = JSVAL_TRUE;
2071 0                     break;
2072                 }
2073 0                 if (pn4->pn_op == JSOP_FALSE) {
2074 0                     pn3->pn_val = JSVAL_FALSE;
2075 0                     break;
2076                 }
2077                 /* FALL THROUGH */
2078               default:
2079 0                 switchOp = JSOP_CONDSWITCH;
2080 0                 continue;
2081             }
2082
2083 0             JS_ASSERT(JSVAL_IS_NUMBER(pn3->pn_val) ||
2084                       JSVAL_IS_STRING(pn3->pn_val) ||
2085                       JSVAL_IS_BOOLEAN(pn3->pn_val));
2086
2087 0             if (switchOp != JSOP_TABLESWITCH)
2088 0                 continue;
2089 0             if (!JSVAL_IS_INT(pn3->pn_val)) {
2090 0                 switchOp = JSOP_LOOKUPSWITCH;
2091 0                 continue;
2092             }
2093 0             i = JSVAL_TO_INT(pn3->pn_val);
2094 0             if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) {
2095 0                 switchOp = JSOP_LOOKUPSWITCH;
2096 0                 continue;
2097             }
2098 0             if (i < low)
2099 0                 low = i;
2100 0             if (high < i)
2101 0                 high = i;
2102
2103             /*
2104              * Check for duplicates, which require a JSOP_LOOKUPSWITCH.
2105              * We bias i by 65536 if it's negative, and hope that's a rare
2106              * case (because it requires a malloc'd bitmap).
2107              */
2108 0             if (i < 0)
2109 0                 i += JS_BIT(16);
2110 0             if (i >= intmap_bitlen) {
2111 0                 if (!intmap &&
2112                     i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) {
2113 0                     intmap = intmap_space;
2114 0                     intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2;
2115                 } else {
2116                     /* Just grab 8K for the worst-case bitmap. */
2117 0                     intmap_bitlen = JS_BIT(16);
2118 0                     intmap = (jsbitmap *)
2119                         JS_malloc(cx,
2120                                   (JS_BIT(16) >> JS_BITS_PER_WORD_LOG2)
2121                                   * sizeof(jsbitmap));
2122 0                     if (!intmap) {
2123 0                         JS_ReportOutOfMemory(cx);
2124 0                         return JS_FALSE;
2125                     }
2126                 }
2127 0                 memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2);
2128             }
2129 0             if (JS_TEST_BIT(intmap, i)) {
2130 0                 switchOp = JSOP_LOOKUPSWITCH;
2131 0                 continue;
2132             }
2133 0             JS_SET_BIT(intmap, i);
2134         }
2135
2136       release:
2137 0         if (intmap && intmap != intmap_space)
2138 0             JS_free(cx, intmap);
2139 0         if (!ok)
2140 0             return JS_FALSE;
2141
2142         /*
2143          * Compute table length and select lookup instead if overlarge or
2144          * more than half-sparse.
2145          */
2146 0         if (switchOp == JSOP_TABLESWITCH) {
2147 0             tableLength = (uint32)(high - low + 1);
2148 0             if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
2149 0                 switchOp = JSOP_LOOKUPSWITCH;
2150         }
2151     }
2152
2153     /*
2154      * Emit a note with two offsets: first tells total switch code length,
2155      * second tells offset to first JSOP_CASE if condswitch.
2156      */
2157 0     noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0);
2158 0     if (noteIndex < 0)
2159 0         return JS_FALSE;
2160
2161 0     if (switchOp == JSOP_CONDSWITCH) {
2162         /*
2163          * 0 bytes of immediate for unoptimized ECMAv2 switch.
2164          */
2165 0         switchSize = 0;
2166 0     } else if (switchOp == JSOP_TABLESWITCH) {
2167         /*
2168          * 3 offsets (len, low, high) before the table, 1 per entry.
2169          */
2170 0         switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
2171     } else {
2172         /*
2173          * JSOP_LOOKUPSWITCH:
2174          * 1 offset (len) and 1 atom index (npairs) before the table,
2175          * 1 atom index and 1 jump offset per entry.
2176          */
2177 0         switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN +
2178                               (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount);
2179     }
2180
2181     /*
2182      * Emit switchOp followed by switchSize bytes of jump or lookup table.
2183      *
2184      * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial
2185      * to emit the immediate operand(s) by which bytecode readers such as
2186      * BuildSpanDepTable discover the length of the switch opcode *before*
2187      * calling js_SetJumpOffset (which may call BuildSpanDepTable).  It's
2188      * also important to zero all unknown jump offset immediate operands,
2189      * so they can be converted to span dependencies with null targets to
2190      * be computed later (js_EmitN zeros switchSize bytes after switchOp).
2191      */
2192 0     if (js_EmitN(cx, cg, switchOp, switchSize) < 0)
2193 0         return JS_FALSE;
2194
2195 0     off = -1;
2196 0     if (switchOp == JSOP_CONDSWITCH) {
2197 0         intN caseNoteIndex = -1;
2198
2199         /* Emit code for evaluating cases and jumping to case statements. */
2200 0         for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2201 0             pn4 = pn3->pn_left;
2202 0             if (pn4 && !js_EmitTree(cx, cg, pn4))
2203 0                 return JS_FALSE;
2204 0             if (caseNoteIndex >= 0) {
2205                 /* off is the previous JSOP_CASE's bytecode offset. */
2206 0                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0,
2207                                          CG_OFFSET(cg) - off)) {
2208 0                     return JS_FALSE;
2209                 }
2210             }
2211 0             if (pn3->pn_type == TOK_DEFAULT)
2212 0                 continue;
2213 0             caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
2214 0             if (caseNoteIndex < 0)
2215 0                 return JS_FALSE;
2216 0             off = EmitJump(cx, cg, JSOP_CASE, 0);
2217 0             if (off < 0)
2218 0                 return JS_FALSE;
2219 0             pn3->pn_offset = off;
2220 0             if (pn3 == pn2->pn_head) {
2221                 /* Switch note's second offset is to first JSOP_CASE. */
2222 0                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
2223                                          off - top)) {
2224 0                     return JS_FALSE;
2225                 }
2226             }
2227         }
2228
2229         /* Emit default even if no explicit default statement. */
2230 0         defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0);
2231 0         if (defaultOffset < 0)
2232 0             return JS_FALSE;
2233     } else {
2234 0         pc = CG_CODE(cg, top + JUMP_OFFSET_LEN);
2235
2236 0         if (switchOp == JSOP_TABLESWITCH) {
2237             /* Fill in switch bounds, which we know fit in 16-bit offsets. */
2238 0             SET_JUMP_OFFSET(pc, low);
2239 0             pc += JUMP_OFFSET_LEN;
2240 0             SET_JUMP_OFFSET(pc, high);
2241 0             pc += JUMP_OFFSET_LEN;
2242
2243             /*
2244              * Use malloc to avoid arena bloat for programs with many switches.
2245              * We free table if non-null at label out, so all control flow must
2246              * exit this function through goto out or goto bad.
2247              */
2248 0             tableSize = (size_t)tableLength * sizeof *table;
2249 0             table = (JSParseNode **) JS_malloc(cx, tableSize);
2250 0             if (!table)
2251 0                 return JS_FALSE;
2252 0             memset(table, 0, tableSize);
2253 0             for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2254 0                 if (pn3->pn_type == TOK_DEFAULT)
2255 0                     continue;
2256 0                 i = JSVAL_TO_INT(pn3->pn_val);
2257 0                 i -= low;
2258 0                 JS_ASSERT((uint32)i < tableLength);
2259 0                 table[i] = pn3;
2260             }
2261         } else {
2262 0             JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH);
2263
2264             /* Fill in the number of cases. */
2265 0             SET_ATOM_INDEX(pc, caseCount);
2266 0             pc += ATOM_INDEX_LEN;
2267         }
2268
2269         /*
2270          * After this point, all control flow involving JSOP_TABLESWITCH
2271          * must set ok and goto out to exit this function.  To keep things
2272          * simple, all switchOp cases exit that way.
2273          */
2274 0         if (constPropagated) {
2275             /*
2276              * Skip switchOp, as we are not setting jump offsets in the two
2277              * for loops below.  We'll restore CG_NEXT(cg) from savepc after,
2278              * unless there was an error.
2279              */
2280 0             savepc = CG_NEXT(cg);
2281 0             CG_NEXT(cg) = pc + 1;
2282 0             if (switchOp == JSOP_TABLESWITCH) {
2283 0                 for (i = 0; i < (jsint)tableLength; i++) {
2284 0                     pn3 = table[i];
2285 0                     if (pn3 &&
2286                         (pn4 = pn3->pn_left) != NULL &&
2287                         pn4->pn_type == TOK_NAME) {
2288                         /* Note a propagated constant with the const's name. */
2289 0                         JS_ASSERT(!pn4->pn_expr);
2290 0                         ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList);
2291 0                         if (!ale)
2292 0                             goto bad;
2293 0                         CG_NEXT(cg) = pc;
2294 0                         if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t)
2295                                            ALE_INDEX(ale)) < 0) {
2296 0                             goto bad;
2297                         }
2298                     }
2299 0                     pc += JUMP_OFFSET_LEN;
2300                 }
2301             } else {
2302 0                 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2303 0                     pn4 = pn3->pn_left;
2304 0                     if (pn4 && pn4->pn_type == TOK_NAME) {
2305                         /* Note a propagated constant with the const's name. */
2306 0                         JS_ASSERT(!pn4->pn_expr);
2307 0                         ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList);
2308 0                         if (!ale)
2309 0                             goto bad;
2310 0                         CG_NEXT(cg) = pc;
2311 0                         if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t)
2312                                            ALE_INDEX(ale)) < 0) {
2313 0                             goto bad;
2314                         }
2315                     }
2316 0                     pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN;
2317                 }
2318             }
2319 0             CG_NEXT(cg) = savepc;
2320         }
2321     }
2322
2323     /* Emit code for each case's statements, copying pn_offset up to pn3. */
2324 0     for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2325 0         if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT)
2326 0             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset);
2327 0         pn4 = pn3->pn_right;
2328 0         ok = js_EmitTree(cx, cg, pn4);
2329 0         if (!ok)
2330 0             goto out;
2331 0         pn3->pn_offset = pn4->pn_offset;
2332 0         if (pn3->pn_type == TOK_DEFAULT)
2333 0             off = pn3->pn_offset - top;
2334     }
2335
2336 0     if (!hasDefault) {
2337         /* If no default case, offset for default is to end of switch. */
2338 0         off = CG_OFFSET(cg) - top;
2339     }
2340
2341     /* We better have set "off" by now. */
2342 0     JS_ASSERT(off != -1);
2343
2344     /* Set the default offset (to end of switch if no default). */
2345 0     if (switchOp == JSOP_CONDSWITCH) {
2346 0         pc = NULL;
2347 0         JS_ASSERT(defaultOffset != -1);
2348 0         ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset),
2349                               off - (defaultOffset - top));
2350 0         if (!ok)
2351 0             goto out;
2352     } else {
2353 0         pc = CG_CODE(cg, top);
2354 0         ok = js_SetJumpOffset(cx, cg, pc, off);
2355 0         if (!ok)
2356 0             goto out;
2357 0         pc += JUMP_OFFSET_LEN;
2358     }
2359
2360     /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
2361 0     off = CG_OFFSET(cg) - top;
2362 0     ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off);
2363 0     if (!ok)
2364 0         goto out;
2365
2366 0     if (switchOp == JSOP_TABLESWITCH) {
2367         /* Skip over the already-initialized switch bounds. */
2368 0         pc += 2 * JUMP_OFFSET_LEN;
2369
2370         /* Fill in the jump table, if there is one. */
2371 0         for (i = 0; i < (jsint)tableLength; i++) {
2372 0             pn3 = table[i];
2373 0             off = pn3 ? pn3->pn_offset - top : 0;
2374 0             ok = js_SetJumpOffset(cx, cg, pc, off);
2375 0             if (!ok)
2376 0                 goto out;
2377 0             pc += JUMP_OFFSET_LEN;
2378         }
2379 0     } else if (switchOp == JSOP_LOOKUPSWITCH) {
2380         /* Skip over the already-initialized number of cases. */
2381 0         pc += ATOM_INDEX_LEN;
2382
2383 0         for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2384 0             if (pn3->pn_type == TOK_DEFAULT)
2385 0                 continue;
2386 0             atom = js_AtomizeValue(cx, pn3->pn_val, 0);
2387 0             if (!atom)
2388 0                 goto bad;
2389 0             ale = js_IndexAtom(cx, atom, &cg->atomList);
2390 0             if (!ale)
2391 0                 goto bad;
2392 0             SET_ATOM_INDEX(pc, ALE_INDEX(ale));
2393 0             pc += ATOM_INDEX_LEN;
2394
2395 0             off = pn3->pn_offset - top;
2396 0             ok = js_SetJumpOffset(cx, cg, pc, off);
2397 0             if (!ok)
2398 0                 goto out;
2399 0             pc += JUMP_OFFSET_LEN;
2400         }
2401     }
2402
2403 out:
2404 0     if (table)
2405 0         JS_free(cx, table);
2406 0     return ok && js_PopStatementCG(cx, cg);
2407
2408 bad:
2409 0     ok = JS_FALSE;
2410 0     goto out;
2411 }
2412 #endif /* JS_HAS_SWITCH_STATEMENT */
2413
2414 JSBool
2415 js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
2416                     JSFunction *fun)
2417 3672 {
2418 3672     JSStackFrame *fp, frame;
2419 3672     JSObject *funobj;
2420 3672     JSBool ok;
2421
2422 3672     if (!js_AllocTryNotes(cx, cg))
2423 0         return JS_FALSE;
2424
2425 3672     fp = cx->fp;
2426 3672     funobj = fun->object;
2427 3672     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
2428                       fp->scopeChain != funobj));
2429 3672     memset(&frame, 0, sizeof frame);
2430 3672     frame.fun = fun;
2431 3672     frame.varobj = frame.scopeChain = funobj;
2432 3672     frame.down = fp;
2433 3672     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
2434                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
2435                   : JSFRAME_COMPILING;
2436 3672     cx->fp = &frame;
2437 3672     ok = js_EmitTree(cx, cg, body);
2438 3672     cx->fp = fp;
2439 3672     if (!ok)
2440 0         return JS_FALSE;
2441
2442 3672     fun->script = js_NewScriptFromCG(cx, cg, fun);
2443 3672     if (!fun->script)
2444 0         return JS_FALSE;
2445 3672     if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
2446 0         fun->flags |= JSFUN_HEAVYWEIGHT;
2447 3672     return JS_TRUE;
2448 }
2449
2450 /* A macro for inlining at the top of js_EmitTree (whence it came). */
2451 #define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn)                                  \
2452     JS_BEGIN_MACRO                                                            \
2453         uintN line_ = (pn)->pn_pos.begin.lineno;                              \
2454         uintN delta_ = line_ - CG_CURRENT_LINE(cg);                           \
2455         if (delta_ != 0) {                                                    \
2456             /*                                                                \
2457              * Encode any change in the current source line number by using   \
2458              * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \
2459              * whichever consumes less space.                                 \
2460              *                                                                \
2461              * NB: We handle backward line number deltas (possible with for   \
2462              * loops where the update part is emitted after the body, but its \
2463              * line number is <= any line number in the body) here by letting \
2464              * unsigned delta_ wrap to a very large number, which triggers a  \
2465              * SRC_SETLINE.                                                   \
2466              */                                                               \
2467             CG_CURRENT_LINE(cg) = line_;                                      \
2468             if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \
2469                 if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\
2470                     return JS_FALSE;                                          \
2471             } else {                                                          \
2472                 do {                                                          \
2473                     if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)               \
2474                         return JS_FALSE;                                      \
2475                 } while (--delta_ != 0);                                      \
2476             }                                                                 \
2477         }                                                                     \
2478     JS_END_MACRO
2479
2480 /* A function, so that we avoid macro-bloating all the other callsites. */
2481 static JSBool
2482 UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
2483 3869 {
2484 3869     UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
2485 3869     return JS_TRUE;
2486 }
2487
2488 JSBool
2489 js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
2490 1320700 {
2491 1320700     JSBool ok, useful, wantval;
2492 1320700     JSStmtInfo *stmt, stmtInfo;
2493 1320700     ptrdiff_t top, off, tmp, beq, jmp;
2494 1320700     JSParseNode *pn2, *pn3;
2495 1320700     JSAtom *atom;
2496 1320700     JSAtomListElement *ale;
2497 1320700     jsatomid atomIndex;
2498 1320700     intN noteIndex;
2499 1320700     JSSrcNoteType noteType;
2500 1320700     jsbytecode *pc;
2501 1320700     JSOp op;
2502 1320700     uint32 argc;
2503 1320700     int stackDummy;
2504
2505 1320700     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
2506 0         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
2507 0         return JS_FALSE;
2508     }
2509
2510 1320700     ok = JS_TRUE;
2511 1320700     cg->emitLevel++;
2512 1320700     pn->pn_offset = top = CG_OFFSET(cg);
2513
2514     /* Emit notes to tell the current bytecode's source line number. */
2515 1320700     UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
2516
2517 1320700     switch (pn->pn_type) {
2518       case TOK_FUNCTION:
2519       {
2520 3672         void *cg2mark;
2521 3672         JSCodeGenerator *cg2;
2522 3672         JSFunction *fun;
2523
2524         /* Generate code for the function's body. */
2525 3672         cg2mark = JS_ARENA_MARK(&cx->tempPool);
2526 3672         JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool);
2527 3672         if (!cg2) {
2528 0             JS_ReportOutOfMemory(cx);
2529 0             return JS_FALSE;
2530         }
2531 3672         if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool,
2532                                   cg->filename, pn->pn_pos.begin.lineno,
2533                                   cg->principals)) {
2534 0             return JS_FALSE;
2535         }
2536 3672         cg2->treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION;
2537 3672         cg2->treeContext.tryCount = pn->pn_tryCount;
2538 3672         cg2->parent = cg;
2539 3672         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
2540 3672         if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun))
2541 0             return JS_FALSE;
2542
2543         /*
2544          * We need an activation object if an inner peeks out, or if such
2545          * inner-peeking caused one of our inners to become heavyweight.
2546          */
2547 3672         if (cg2->treeContext.flags &
2548             (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {
2549 3434             cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
2550         }
2551 3672         js_FinishCodeGenerator(cx, cg2);
2552 3672         JS_ARENA_RELEASE(&cx->tempPool, cg2mark);
2553
2554         /* Make the function object a literal in the outer script's pool. */
2555 3672         ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList);
2556 3672         if (!ale)
2557 0             return JS_FALSE;
2558 3672         atomIndex = ALE_INDEX(ale);
2559
2560 #if JS_HAS_LEXICAL_CLOSURE
2561         /* Emit a bytecode pointing to the closure object in its immediate. */
2562 3672         if (pn->pn_op != JSOP_NOP) {
2563 0             EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
2564 3672             break;
2565         }
2566 #endif
2567
2568         /* Top-level named functions need a nop for decompilation. */
2569 3672         noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);
2570 3672         if (noteIndex < 0 ||
2571             js_Emit1(cx, cg, JSOP_NOP) < 0) {
2572 0             return JS_FALSE;
2573         }
2574
2575         /*
2576          * Top-levels also need a prolog op to predefine their names in the
2577          * variable object, or if local, to fill their stack slots.
2578          */
2579 3672         CG_SWITCH_TO_PROLOG(cg);
2580
2581 #if JS_HAS_LEXICAL_CLOSURE
2582 3672         if (cg->treeContext.flags & TCF_IN_FUNCTION) {
2583 0             JSObject *obj, *pobj;
2584 0             JSScopeProperty *sprop;
2585 0             uintN slot;
2586
2587 0             obj = OBJ_GET_PARENT(cx, fun->object);
2588 0             if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj,
2589                                    (JSProperty **)&sprop)) {
2590 0                 return JS_FALSE;
2591             }
2592 0             JS_ASSERT(sprop && pobj == obj);
2593 0             slot = sprop->shortid;
2594 0             OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
2595
2596             /* Emit [JSOP_DEFLOCALFUN, local variable slot, atomIndex]. */
2597 0             off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, VARNO_LEN+ATOM_INDEX_LEN);
2598 0             if (off < 0)
2599 0                 return JS_FALSE;
2600 0             pc = CG_CODE(cg, off);
2601 0             SET_VARNO(pc, slot);
2602 0             pc += VARNO_LEN;
2603 0             SET_ATOM_INDEX(pc, atomIndex);
2604         } else
2605 #endif
2606 3672             EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
2607
2608 3672         CG_SWITCH_TO_MAIN(cg);
2609 3672         break;
2610       }
2611
2612 #if JS_HAS_EXPORT_IMPORT
2613       case TOK_EXPORT:
2614         pn2 = pn->pn_head;
2615         if (pn2->pn_type == TOK_STAR) {
2616             /*
2617              * 'export *' must have no other elements in the list (what would
2618              * be the point?).
2619              */
2620             if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0)
2621                 return JS_FALSE;
2622         } else {
2623             /*
2624              * If not 'export *', the list consists of NAME nodes identifying
2625              * properties of the variables object to flag as exported.
2626              */
2627             do {
2628                 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
2629                 if (!ale)
2630                     return JS_FALSE;
2631                 EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale));
2632             } while ((pn2 = pn2->pn_next) != NULL);
2633         }
2634         break;
2635
2636       case TOK_IMPORT:
2637         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2638             /*
2639              * Each subtree on an import list is rooted by a DOT or LB node.
2640              * A DOT may have a null pn_atom member, in which case pn_op must
2641              * be JSOP_IMPORTALL -- see EmitPropOp above.
2642              */
2643             if (!js_EmitTree(cx, cg, pn2))
2644                 return JS_FALSE;
2645         }
2646         break;
2647 #endif /* JS_HAS_EXPORT_IMPORT */
2648
2649       case TOK_IF:
2650         /* Initialize so we can detect else-if chains and avoid recursion. */
2651 11378         stmtInfo.type = STMT_IF;
2652 11378         beq = jmp = -1;
2653 11378         noteIndex = -1;
2654
2655       if_again:
2656         /* Emit code for the condition before pushing stmtInfo. */
2657 13646         if (!js_EmitTree(cx, cg, pn->pn_kid1))
2658 0             return JS_FALSE;
2659 13646         if (stmtInfo.type == STMT_IF) {
2660 11378             js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF,
2661                              CG_OFFSET(cg));
2662         } else {
2663             /*
2664              * We came here from the goto further below that detects else-if
2665              * chains, so we must mutate stmtInfo back into a STMT_IF record.
2666              * Also (see below for why) we need a note offset for SRC_IF_ELSE
2667              * to help the decompiler.
2668              */
2669 2268             JS_ASSERT(stmtInfo.type == STMT_ELSE);
2670 2268             stmtInfo.type = STMT_IF;
2671 2268             if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
2672 0                 return JS_FALSE;
2673         }
2674
2675         /* Emit an annotated branch-if-false around the then part. */
2676 13646         pn3 = pn->pn_kid3;
2677 13646         noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
2678 13646         if (noteIndex < 0)
2679 0             return JS_FALSE;
2680 13646         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2681 13646         if (beq < 0)
2682 0             return JS_FALSE;
2683
2684         /* Emit code for the then and optional else parts. */
2685 13646         if (!js_EmitTree(cx, cg, pn->pn_kid2))
2686 0             return JS_FALSE;
2687 13646         if (pn3) {
2688             /* Modify stmtInfo so we know we're in the else part. */
2689 5411             stmtInfo.type = STMT_ELSE;
2690
2691             /*
2692              * Emit a JSOP_BACKPATCH op to jump from the end of our then part
2693              * around the else part.  The js_PopStatementCG call at the bottom
2694              * of this switch case will fix up the backpatch chain linked from
2695              * stmtInfo.breaks.
2696              */
2697 5411             jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL);
2698 5411             if (jmp < 0)
2699 0                 return JS_FALSE;
2700
2701             /* Ensure the branch-if-false comes here, then emit the else. */
2702 5411             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2703 5411             if (pn3->pn_type == TOK_IF) {
2704 2268                 pn = pn3;
2705 2268                 goto if_again;
2706             }
2707
2708 3143             if (!js_EmitTree(cx, cg, pn3))
2709 0                 return JS_FALSE;
2710
2711             /*
2712              * Annotate SRC_IF_ELSE with the offset from branch to jump, for
2713              * the decompiler's benefit.  We can't just "back up" from the pc
2714              * of the else clause, because we don't know whether an extended
2715              * jump was required to leap from the end of the then clause over
2716              * the else clause.
2717              */
2718 3143             if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
2719 0                 return JS_FALSE;
2720         } else {
2721             /* No else part, fixup the branch-if-false to come here. */
2722 8235             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2723         }
2724 11378         ok = js_PopStatementCG(cx, cg);
2725 11378         break;
2726
2727 #if JS_HAS_SWITCH_STATEMENT
2728       case TOK_SWITCH:
2729         /* Out of line to avoid bloating js_EmitTree's stack frame size. */
2730 0         ok = EmitSwitch(cx, cg, pn, &stmtInfo);
2731 0         break;
2732 #endif /* JS_HAS_SWITCH_STATEMENT */
2733
2734       case TOK_WHILE:
2735 238         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
2736 238         if (!js_EmitTree(cx, cg, pn->pn_left))
2737 0             return JS_FALSE;
2738 238         noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
2739 238         if (noteIndex < 0)
2740 0             return JS_FALSE;
2741 238         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2742 238         if (beq < 0)
2743 0             return JS_FALSE;
2744 238         if (!js_EmitTree(cx, cg, pn->pn_right))
2745 0             return JS_FALSE;
2746 238         jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
2747 238         if (jmp < 0)
2748 0             return JS_FALSE;
2749 238         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2750 238         if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
2751 0             return JS_FALSE;
2752 238         ok = js_PopStatementCG(cx, cg);
2753 238         break;
2754
2755 #if JS_HAS_DO_WHILE_LOOP
2756       case TOK_DO:
2757         /* Emit an annotated nop so we know to decompile a 'do' keyword. */
2758 0         if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||
2759             js_Emit1(cx, cg, JSOP_NOP) < 0) {
2760 0             return JS_FALSE;
2761         }
2762
2763         /* Compile the loop body. */
2764 0         top = CG_OFFSET(cg);
2765 0         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top);
2766 0         if (!js_EmitTree(cx, cg, pn->pn_left))
2767 0             return JS_FALSE;
2768
2769         /* Set loop and enclosing label update offsets, for continue. */
2770 0         stmt = &stmtInfo;
2771 0         do {
2772 0             stmt->update = CG_OFFSET(cg);
2773 0         } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
2774
2775         /* Compile the loop condition, now that continues know where to go. */
2776 0         if (!js_EmitTree(cx, cg, pn->pn_right))
2777 0             return JS_FALSE;
2778
2779         /*
2780          * No source note needed, because JSOP_IFNE is used only for do-while.
2781          * If we ever use JSOP_IFNE for other purposes, we can still avoid yet
2782          * another note here, by storing (jmp - top) in the SRC_WHILE note's
2783          * offset, and fetching that delta in order to decompile recursively.
2784          */
2785 0         if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0)
2786 0             return JS_FALSE;
2787 0         ok = js_PopStatementCG(cx, cg);
2788 0         break;
2789 #endif /* JS_HAS_DO_WHILE_LOOP */
2790
2791       case TOK_FOR:
2792 1561         beq = 0;                /* suppress gcc warnings */
2793 1561         pn2 = pn->pn_left;
2794 1561         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);
2795
2796 1561         if (pn2->pn_type == TOK_IN) {
2797             /* Set stmtInfo type for later testing. */
2798 1493             stmtInfo.type = STMT_FOR_IN_LOOP;
2799 1493             noteIndex = -1;
2800
2801             /* If the left part is var x = i, bind x, evaluate i, and pop. */
2802 1493             pn3 = pn2->pn_left;
2803 1493             if (pn3->pn_type == TOK_VAR && pn3->pn_head->pn_expr) {
2804 0                 if (!js_EmitTree(cx, cg, pn3))
2805 0                     return JS_FALSE;
2806                 /* Set pn3 to the variable name, to avoid another var note. */
2807 0                 pn3 = pn3->pn_head;
2808 1493                 JS_ASSERT(pn3->pn_type == TOK_NAME);
2809             }
2810
2811             /* Emit a push to allocate the iterator. */
2812 1493             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
2813 0                 return JS_FALSE;
2814
2815             /* Compile the object expression to the right of 'in'. */
2816 1493             if (!js_EmitTree(cx, cg, pn2->pn_right))
2817 0                 return JS_FALSE;
2818 1493             if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0)
2819 0                 return JS_FALSE;
2820
2821 1493             top = CG_OFFSET(cg);
2822 1493             SET_STATEMENT_TOP(&stmtInfo, top);
2823
2824             /* Compile a JSOP_FOR* bytecode based on the left hand side. */
2825 1493             switch (pn3->pn_type) {
2826               case TOK_VAR:
2827 1493                 pn3 = pn3->pn_head;
2828 1493                 if (js_NewSrcNote(cx, cg, SRC_VAR) < 0)
2829 0                     return JS_FALSE;
2830                 /* FALL THROUGH */
2831               case TOK_NAME:
2832 1493                 pn3->pn_op = JSOP_FORNAME;
2833 1493                 if (!LookupArgOrVar(cx, &cg->treeContext, pn3))
2834 0                     return JS_FALSE;
2835 1493                 op = pn3->pn_op;
2836 1493                 if (pn3->pn_slot >= 0) {
2837 782                     if (pn3->pn_attrs & JSPROP_READONLY)
2838 0                         op = JSOP_GETVAR;
2839 782                     atomIndex = (jsatomid) pn3->pn_slot;
2840 782                     EMIT_ATOM_INDEX_OP(op, atomIndex);
2841                 } else {
2842 711                     if (!EmitAtomOp(cx, pn3, op, cg))
2843 0                         return JS_FALSE;
2844                 }
2845 0                 break;
2846
2847               case TOK_DOT:
2848 0                 if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg))
2849 0                     return JS_FALSE;
2850 0                 break;
2851
2852               case TOK_LB:
2853                 /*
2854                  * We separate the first/next bytecode from the enumerator
2855                  * variable binding to avoid any side-effects in the index
2856                  * expression (e.g., for (x[i++] in {}) should not bind x[i]
2857                  * or increment i at all).
2858                  */
2859 0                 if (!js_Emit1(cx, cg, JSOP_FORELEM))
2860 0                     return JS_FALSE;
2861
2862                 /*
2863                  * Emit a SRC_WHILE note with offset telling the distance to
2864                  * the loop-closing jump (we can't reckon from the branch at
2865                  * the top of the loop, because the loop-closing jump might
2866                  * need to be an extended jump, independent of whether the
2867                  * branch is short or long).
2868                  */
2869 0                 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
2870 0                 if (noteIndex < 0)
2871 0                     return JS_FALSE;
2872 0                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2873 0                 if (beq < 0)
2874 0                     return JS_FALSE;
2875
2876                 /* Now that we're safely past the IFEQ, commit side effects. */
2877 0                 if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))
2878 0                     return JS_FALSE;
2879 1493                 break;
2880
2881               default:
2882 1493                 JS_ASSERT(0);
2883             }
2884 1493             if (pn3->pn_type != TOK_LB) {
2885                 /* Annotate so the decompiler can find the loop-closing jump. */
2886 1493                 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
2887 1493                 if (noteIndex < 0)
2888 0                     return JS_FALSE;
2889
2890                 /* Pop and test the loop condition generated by JSOP_FOR*. */
2891 1493                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2892 1493                 if (beq < 0)
2893 0                     return JS_FALSE;
2894             }
2895         } else {
2896 68             if (!pn2->pn_kid1) {
2897                 /* No initializer: emit an annotated nop for the decompiler. */
2898 0                 op = JSOP_NOP;
2899             } else {
2900 68                 if (!js_EmitTree(cx, cg, pn2->pn_kid1))
2901 0                     return JS_FALSE;
2902 68                 op = JSOP_POP;
2903             }
2904 68             noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
2905 68             if (noteIndex < 0 ||
2906                 js_Emit1(cx, cg, op) < 0) {
2907 0                 return JS_FALSE;
2908             }
2909
2910 68             top = CG_OFFSET(cg);
2911 68             SET_STATEMENT_TOP(&stmtInfo, top);
2912 68             if (!pn2->pn_kid2) {
2913                 /* No loop condition: flag this fact in the source notes. */
2914 0                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0))
2915 0                     return JS_FALSE;
2916             } else {
2917 68                 if (!js_EmitTree(cx, cg, pn2->pn_kid2))
2918 0                     return JS_FALSE;
2919 68                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
2920                                          CG_OFFSET(cg) - top)) {
2921 0                     return JS_FALSE;
2922                 }
2923 68                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2924 68                 if (beq < 0)
2925 0                     return JS_FALSE;
2926             }
2927
2928             /* Set pn3 (used below) here to avoid spurious gcc warnings. */
2929 68             pn3 = pn2->pn_kid3;
2930         }
2931
2932         /* Emit code for the loop body. */
2933 1561         if (!js_EmitTree(cx, cg, pn->pn_right))
2934 0             return JS_FALSE;
2935
2936 1561         if (pn2->pn_type != TOK_IN) {
2937             /* Set the second note offset so we can find the update part. */
2938 68             JS_ASSERT(noteIndex != -1);
2939 68             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
2940                                      CG_OFFSET(cg) - top)) {
2941 0                 return JS_FALSE;
2942             }
2943
2944 68             if (pn3) {
2945                 /* Set loop and enclosing "update" offsets, for continue. */
2946 68                 stmt = &stmtInfo;
2947 68                 do {
2948 68                     stmt->update = CG_OFFSET(cg);
2949 68                 } while ((stmt = stmt->down) != NULL &&
2950                          stmt->type == STMT_LABEL);
2951
2952 68                 if (!js_EmitTree(cx, cg, pn3))
2953 0                     return JS_FALSE;
2954 68                 if (js_Emit1(cx, cg, JSOP_POP) < 0)
2955 0                     return JS_FALSE;
2956
2957                 /* Restore the absolute line number for source note readers. */
2958 68                 off = (ptrdiff_t) pn->pn_pos.end.lineno;
2959 68                 if (CG_CURRENT_LINE(cg) != (uintN) off) {
2960 68                     if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
2961 0                         return JS_FALSE;
2962 68                     CG_CURRENT_LINE(cg) = (uintN) off;
2963                 }
2964             }
2965
2966             /* The third note offset helps us find the loop-closing jump. */
2967 68             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2,
2968                                      CG_OFFSET(cg) - top)) {
2969 0                 return JS_FALSE;
2970             }
2971         }
2972
2973         /* Emit the loop-closing jump and fixup all jump offsets. */
2974 1561         jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
2975 1561         if (jmp < 0)
2976 0             return JS_FALSE;
2977 1561         if (beq > 0)
2978 1561             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2979 1561         if (pn2->pn_type == TOK_IN) {
2980             /* Set the SRC_WHILE note offset so we can find the closing jump. */
2981 1493             JS_ASSERT(noteIndex != -1);
2982 1493             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq))
2983 0                 return JS_FALSE;
2984         }
2985
2986         /* Now fixup all breaks and continues (before for/in's final POP2). */
2987 1561         if (!js_PopStatementCG(cx, cg))
2988 0             return JS_FALSE;
2989
2990 1561         if (pn2->pn_type == TOK_IN) {
2991             /*
2992              * Generate the object and iterator pop opcodes after popping the
2993              * stmtInfo stack, so breaks will go to this pop bytecode.
2994              */
2995 1493             if (pn3->pn_type != TOK_LB) {
2996 1493                 if (js_Emit1(cx, cg, JSOP_POP2) < 0)
2997 0                     return JS_FALSE;
2998             } else {
2999                 /*
3000                  * With 'for(x[i]...)', there's only the object on the stack,
3001                  * so we need to hide the pop.
3002                  */
3003 0                 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
3004 0                     return JS_FALSE;
3005 0                 if (js_Emit1(cx, cg, JSOP_POP) < 0)
3006 0                     return JS_FALSE;
3007             }
3008         }
3009 34         break;
3010
3011       case TOK_BREAK:
3012 34         stmt = cg->treeContext.topStmt;
3013 34         atom = pn->pn_atom;
3014 34         if (atom) {
3015 0             ale = js_IndexAtom(cx, atom, &cg->atomList);
3016 0             if (!ale)
3017 0                 return JS_FALSE;
3018 0             while (stmt->type != STMT_LABEL || stmt->label != atom)
3019 0                 stmt = stmt->down;
3020 0             noteType = SRC_BREAK2LABEL;
3021         } else {
3022 34             ale = NULL;
3023 68             while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
3024 34                 stmt = stmt->down;
3025 34             noteType = SRC_NULL;
3026         }
3027
3028 34         if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0)
3029 0             return JS_FALSE;
3030 493         break;
3031
3032       case TOK_CONTINUE:
3033 493         stmt = cg->treeContext.topStmt;
3034 493         atom = pn->pn_atom;
3035 493         if (atom) {
3036             /* Find the loop statement enclosed by the matching label. */
3037 0             JSStmtInfo *loop = NULL;
3038 0             ale = js_IndexAtom(cx, atom, &cg->atomList);
3039 0             if (!ale)
3040 0                 return JS_FALSE;
3041 0             while (stmt->type != STMT_LABEL || stmt->label != atom) {
3042 0                 if (STMT_IS_LOOP(stmt))
3043 0                     loop = stmt;
3044 0                 stmt = stmt->down;
3045             }
3046 0             stmt = loop;
3047 0             noteType = SRC_CONT2LABEL;
3048         } else {
3049 493             ale = NULL;
3050 2006             while (!STMT_IS_LOOP(stmt))
3051 1513                 stmt = stmt->down;
3052 493             noteType = SRC_CONTINUE;
3053         }
3054
3055 493         if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0)
3056 0             return JS_FALSE;
3057 0         break;
3058
3059       case TOK_WITH:
3060 0         if (!js_EmitTree(cx, cg, pn->pn_left))
3061 0             return JS_FALSE;
3062 0         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg));
3063 0         if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
3064 0             return JS_FALSE;
3065 0         if (!js_EmitTree(cx, cg, pn->pn_right))
3066 0             return JS_FALSE;
3067 0         if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
3068 0             return JS_FALSE;
3069 0         ok = js_PopStatementCG(cx, cg);
3070 0         break;
3071
3072 #if JS_HAS_EXCEPTIONS
3073
3074       case TOK_TRY:
3075       {
3076 0         ptrdiff_t start, end, catchStart, finallyCatch, catchJump;
3077 0         JSParseNode *iter;
3078 0         intN depth;
3079
3080         /* Quell GCC overwarnings. */
3081 0         end = catchStart = finallyCatch = catchJump = -1;
3082
3083 /* Emit JSOP_GOTO that points to the first op after the catch/finally blocks */
3084 #define EMIT_CATCH_GOTO(cx, cg, jmp)                                          \
3085     EMIT_BACKPATCH_OP(cx, cg, stmtInfo.catchJump, JSOP_BACKPATCH, jmp)
3086
3087 /* Emit JSOP_GOSUB that points to the finally block. */
3088 #define EMIT_FINALLY_GOSUB(cx, cg, jmp)                                       \
3089     EMIT_BACKPATCH_OP(cx, cg, stmtInfo.gosub, JSOP_BACKPATCH_PUSH, jmp)
3090
3091         /*
3092          * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
3093          * for later fixup.
3094          *
3095          * When a finally block is `active' (STMT_FINALLY on the treeContext),
3096          * non-local jumps (including jumps-over-catches) result in a GOSUB
3097          * being written into the bytecode stream and fixed-up later (c.f.
3098          * EMIT_BACKPATCH_OP and BackPatch).
3099          */
3100 0         js_PushStatement(&cg->treeContext, &stmtInfo,
3101                          pn->pn_kid3 ? STMT_FINALLY : STMT_BLOCK,
3102                          CG_OFFSET(cg));
3103
3104         /*
3105          * About JSOP_SETSP: an exception can be thrown while the stack is in
3106          * an unbalanced state, and this imbalance causes problems with things
3107          * like function invocation later on.
3108          *
3109          * To fix this, we compute the `balanced' stack depth upon try entry,
3110          * and then restore the stack to this depth when we hit the first catch
3111          * or finally block.  We can't just zero the stack, because things like
3112          * for/in and with that are active upon entry to the block keep state
3113          * variables on the stack.
3114          */
3115 0         depth = cg->stackDepth;
3116
3117         /* Mark try location for decompilation, then emit try block. */
3118 0         if (js_Emit1(cx, cg, JSOP_TRY) < 0)
3119 0             return JS_FALSE;
3120 0         start = CG_OFFSET(cg);
3121 0         if (!js_EmitTree(cx, cg, pn->pn_kid1))
3122 0             return JS_FALSE;
3123
3124         /* GOSUB to finally, if present. */
3125 0         if (pn->pn_kid3) {
3126 0             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
3127 0                 return JS_FALSE;
3128 0             EMIT_FINALLY_GOSUB(cx, cg, jmp);
3129 0             if (jmp < 0)
3130 0                 return JS_FALSE;
3131         }
3132
3133         /* Emit (hidden) jump over catch and/or finally. */
3134 0         if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
3135 0             return JS_FALSE;
3136 0         EMIT_CATCH_GOTO(cx, cg, jmp);
3137 0         if (jmp < 0)
3138 0             return JS_FALSE;
3139
3140 0         end = CG_OFFSET(cg);
3141
3142         /* If this try has a catch block, emit it. */
3143 0         iter = pn->pn_kid2;
3144 0         if (iter) {
3145 0             catchStart = end;
3146
3147             /*
3148              * The emitted code for a catch block looks like:
3149              *
3150              * [ popscope ]                        only if 2nd+ catch block
3151              * name Object
3152              * pushobj
3153              * newinit
3154              * exception
3155              * initcatchvar <atom>
3156              * enterwith
3157              * [< catchguard code >]               if there's a catchguard
3158              * [ifeq <offset to next catch block>]         " "
3159              * < catch block contents >
3160              * leavewith
3161              * goto <end of catch blocks>          non-local; finally applies
3162              *
3163              * If there's no catch block without a catchguard, the last
3164              * <offset to next catch block> points to rethrow code.  This
3165              * code will GOSUB to the finally code if appropriate, and is
3166              * also used for the catch-all trynote for capturing exceptions
3167              * thrown from catch{} blocks.
3168              */
3169 0             for (;;) {
3170 0                 JSStmtInfo stmtInfo2;
3171 0                 JSParseNode *disc;
3172 0                 ptrdiff_t guardnote;
3173
3174 0                 if (!UpdateLineNumberNotes(cx, cg, iter))
3175 0                     return JS_FALSE;
3176
3177 0                 if (catchJump != -1) {
3178                     /* Fix up and clean up previous catch block. */
3179 0                     CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump);
3180
3181                     /* Compensate for the [leavewith]. */
3182 0                     cg->stackDepth++;
3183 0                     JS_ASSERT((uintN) cg->stackDepth <= cg->maxStackDepth);
3184
3185 0                     if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
3186                         js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
3187 0                         return JS_FALSE;
3188                     }
3189                 } else {
3190                     /* Set stack to original depth (see SETSP comment above). */
3191 0                     EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);
3192 0                     cg->stackDepth = depth;
3193                 }
3194
3195                 /* Non-negative guardnote offset is length of catchguard. */
3196 0                 guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
3197 0                 if (guardnote < 0 ||
3198                     js_Emit1(cx, cg, JSOP_NOP) < 0) {
3199 0                     return JS_FALSE;
3200                 }
3201
3202                 /* Construct the scope holder and push it on. */
3203 0                 ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,
3204                                    &cg->atomList);
3205 0                 if (!ale)
3206 0                     return JS_FALSE;
3207 0                 EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
3208
3209 0                 if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 ||
3210                     js_Emit1(cx, cg, JSOP_NEWINIT) < 0 ||
3211                     js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) {
3212 0                     return JS_FALSE;
3213                 }
3214
3215                 /* initcatchvar <atomIndex> */
3216 0                 disc = iter->pn_kid1;
3217 0                 ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList);
3218 0                 if (!ale)
3219 0                     return JS_FALSE;
3220
3221 0                 EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale));
3222 0                 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
3223                     js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) {
3224 0                     return JS_FALSE;
3225                 }
3226
3227                 /* boolean_expr */
3228 0                 if (disc->pn_expr) {
3229 0                     ptrdiff_t guardstart = CG_OFFSET(cg);
3230 0                     if (!js_EmitTree(cx, cg, disc->pn_expr))
3231 0                         return JS_FALSE;
3232 0                     if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0,
3233                                              CG_OFFSET(cg) - guardstart)) {
3234 0                         return JS_FALSE;
3235                     }
3236                     /* ifeq <next block> */
3237 0                     catchJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
3238 0                     if (catchJump < 0)
3239 0                         return JS_FALSE;
3240                 }
3241
3242                 /* Emit catch block. */
3243 0                 js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_CATCH,
3244                                  CG_OFFSET(cg));
3245 0                 stmtInfo2.label = disc->pn_atom;
3246 0                 if (!js_EmitTree(cx, cg, iter->pn_kid3))
3247 0                     return JS_FALSE;
3248 0                 js_PopStatementCG(cx, cg);
3249
3250                 /*
3251                  * Jump over the remaining catch blocks.
3252                  * This counts as a non-local jump, so do the finally thing.
3253                  */
3254
3255                 /* popscope */
3256 0                 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
3257                     js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
3258 0                     return JS_FALSE;
3259                 }
3260
3261                 /* gosub <finally>, if required */
3262 0                 if (pn->pn_kid3) {
3263 0                     EMIT_FINALLY_GOSUB(cx, cg, jmp);
3264 0                     if (jmp < 0)
3265 0                         return JS_FALSE;
3266                 }
3267
3268                 /* This will get fixed up to jump to after catch/finally. */
3269 0                 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
3270 0                     return JS_FALSE;
3271 0                 EMIT_CATCH_GOTO(cx, cg, jmp);
3272 0                 if (jmp < 0)
3273 0                     return JS_FALSE;
3274 0                 if (!iter->pn_kid2)     /* leave iter at last catch */
3275 0                     break;
3276 0                 iter = iter->pn_kid2;
3277             }
3278         }
3279
3280         /*
3281          * We use a [setsp],[gosub],rethrow block for rethrowing when
3282          * there's no unguarded catch, and also for running finally code
3283          * while letting an uncaught exception pass through.
3284          */
3285 0         if (pn->pn_kid3 ||
3286             (catchJump != -1 && iter->pn_kid1->pn_expr)) {
3287             /*
3288              * Emit another stack fixup, because the catch could itself
3289              * throw an exception in an unbalanced state, and the finally
3290              * may need to call functions.  If there is no finally, only
3291              * guarded catches, the rethrow code below nevertheless needs
3292              * stack fixup.
3293              */
3294 0             finallyCatch = CG_OFFSET(cg);
3295 0             EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);
3296 0             cg->stackDepth = depth;
3297
3298             /* Last discriminant jumps to rethrow if none match. */
3299 0             if (catchJump != -1 && iter->pn_kid1->pn_expr)
3300 0                 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump);
3301
3302 0             if (pn->pn_kid3) {
3303 0                 EMIT_FINALLY_GOSUB(cx, cg, jmp);
3304 0                 if (jmp < 0)
3305 0                     return JS_FALSE;
3306 0                 cg->stackDepth = depth;
3307             }
3308
3309 0             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
3310                 js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 ||
3311                 js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
3312                 js_Emit1(cx, cg, JSOP_THROW) < 0) {
3313 0                 return JS_FALSE;
3314             }
3315 0             JS_ASSERT(cg->stackDepth == depth);
3316         }
3317
3318         /*
3319          * If we have a finally, it belongs here, and we have to fix up the
3320          * gosubs that might have been emitted before non-local jumps.
3321          */
3322 0         if (pn->pn_kid3) {
3323 0             if (!BackPatch(cx, cg, stmtInfo.gosub, CG_NEXT(cg), JSOP_GOSUB))
3324 0                 return JS_FALSE;
3325
3326             /*
3327              * The stack budget must be balanced at this point, and we need
3328              * one more slot for the JSOP_RETSUB return address pushed by a
3329              * JSOP_GOSUB opcode that calls this finally clause.
3330              */
3331 0             JS_ASSERT(cg->stackDepth == depth);
3332 0             if ((uintN)++cg->stackDepth > cg->maxStackDepth)
3333 0                 cg->maxStackDepth = cg->stackDepth;
3334
3335             /* Now indicate that we're emitting a subroutine body. */
3336 0             stmtInfo.type = STMT_SUBROUTINE;
3337 0             if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3))
3338 0                 return JS_FALSE;
3339 0             if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||
3340                 !js_EmitTree(cx, cg, pn->pn_kid3) ||
3341                 js_Emit1(cx, cg, JSOP_RETSUB) < 0) {
3342 0                 return JS_FALSE;
3343             }
3344         }
3345 0         js_PopStatementCG(cx, cg);
3346
3347 0         if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
3348             js_Emit1(cx, cg, JSOP_NOP) < 0) {
3349 0             return JS_FALSE;
3350         }
3351
3352         /* Fix up the end-of-try/catch jumps to come here. */
3353 0         if (!BackPatch(cx, cg, stmtInfo.catchJump, CG_NEXT(cg), JSOP_GOTO))
3354 0             return JS_FALSE;
3355
3356         /*
3357          * Add the try note last, to let post-order give us the right ordering
3358          * (first to last for a given nesting level, inner to outer by level).
3359          */
3360 0         if (pn->pn_kid2) {
3361 0             JS_ASSERT(end != -1 && catchStart != -1);
3362 0             if (!js_NewTryNote(cx, cg, start, end, catchStart))
3363 0                 return JS_FALSE;
3364         }
3365
3366         /*
3367          * If we've got a finally, mark try+catch region with additional
3368          * trynote to catch exceptions (re)thrown from a catch block or
3369          * for the try{}finally{} case.
3370          */
3371 0         if (pn->pn_kid3) {
3372 0             JS_ASSERT(finallyCatch != -1);
3373 0             if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch))
3374 0                 return JS_FALSE;
3375         }
3376 8538         break;
3377       }
3378
3379 #endif /* JS_HAS_EXCEPTIONS */
3380
3381       case TOK_VAR:
3382 8538         off = noteIndex = -1;
3383 8595         for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
3384 8595             JS_ASSERT(pn2->pn_type == TOK_NAME);
3385 8595             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3386 0                 return JS_FALSE;
3387 8595             op = pn2->pn_op;
3388 8595             if (op == JSOP_ARGUMENTS) {
3389 0                 JS_ASSERT(!pn2->pn_expr); /* JSOP_ARGUMENTS => no initializer */
3390 #ifdef __GNUC__
3391 0                 atomIndex = 0;            /* quell GCC overwarning */
3392 #endif
3393             } else {
3394 8595                 if (pn2->pn_slot >= 0) {
3395 4726                     atomIndex = (jsatomid) pn2->pn_slot;
3396                 } else {
3397 3869                     ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3398 3869                     if (!ale)
3399 0                         return JS_FALSE;
3400 3869                     atomIndex = ALE_INDEX(ale);
3401
3402 3869                     if (!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
3403                         (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)) {
3404                         /* Emit a prolog bytecode to predefine the variable. */
3405 3869                         CG_SWITCH_TO_PROLOG(cg);
3406 3869                         if (!UpdateLineNumberNotes(cx, cg, pn2))
3407 0                             return JS_FALSE;
3408 3869                         EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
3409 3869                         CG_SWITCH_TO_MAIN(cg);
3410                     }
3411                 }
3412 8595                 if (pn2->pn_expr) {
3413 8467                     if (op == JSOP_SETNAME)
3414 3741                         EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
3415 8467                     pn3 = pn2->pn_expr;
3416 8467                     if (!js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
3417                                                       pn3)) {
3418 0                         return JS_FALSE;
3419                     }
3420 8467                     if (!js_EmitTree(cx, cg, pn3))
3421 0                         return JS_FALSE;
3422                 }
3423             }
3424 8595             if (pn2 == pn->pn_head &&
3425                 js_NewSrcNote(cx, cg,
3426                               (pn->pn_op == JSOP_DEFCONST)
3427                               ? SRC_CONST
3428                               : SRC_VAR) < 0) {
3429 0                 return JS_FALSE;
3430             }
3431 8595             if (op == JSOP_ARGUMENTS) {
3432 0                 if (js_Emit1(cx, cg, op) < 0)
3433 0                     return JS_FALSE;
3434             } else {
3435 8595                 EMIT_ATOM_INDEX_OP(op, atomIndex);
3436             }
3437 8595             tmp = CG_OFFSET(cg);
3438 8595             if (noteIndex >= 0) {
3439 57                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
3440 0                     return JS_FALSE;
3441             }
3442 8595             if (!pn2->pn_next)
3443 8538                 break;
3444 57             off = tmp;
3445 57             noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
3446 57             if (noteIndex < 0 ||
3447                 js_Emit1(cx, cg, JSOP_POP) < 0) {
3448 0                 return JS_FALSE;
3449             }
3450         }
3451 8538         if (pn->pn_extra) {
3452 8470             if (js_Emit1(cx, cg, JSOP_POP) < 0)
3453 0                 return JS_FALSE;
3454         }
3455 2482         break;
3456
3457       case TOK_RETURN:
3458         /* Push a return value */
3459 2482         pn2 = pn->pn_kid;
3460 2482         if (pn2) {
3461 1496             if (!js_EmitTree(cx, cg, pn2))
3462 0                 return JS_FALSE;
3463         } else {
3464 986             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
3465 0                 return JS_FALSE;
3466         }
3467
3468         /*
3469          * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
3470          * JSOP_SETRVAL if there are open try blocks having finally clauses.
3471          * We can't simply transfer control flow to our caller in that case,
3472          * because we must gosub to those clauses from inner to outer, with
3473          * the correct stack pointer (i.e., after popping any with, for/in,
3474          * etc., slots nested inside the finally's try).
3475          */
3476 2482         op = JSOP_RETURN;
3477 2482         if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
3478 0             return JS_FALSE;
3479 2482         if (js_Emit1(cx, cg, op) < 0)
3480 0             return JS_FALSE;
3481 22260         break;
3482
3483       case TOK_LC:
3484 22260         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
3485 121687         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3486 99427             if (!js_EmitTree(cx, cg, pn2))
3487 0                 return JS_FALSE;
3488         }
3489 22260         ok = js_PopStatementCG(cx, cg);
3490 22260         break;
3491
3492       case TOK_SEMI:
3493 266451         pn2 = pn->pn_kid;
3494 266451         if (pn2) {
3495             /*
3496              * Top-level or called-from-a-native JS_Execute/EvaluateScript,
3497              * debugger, and eval frames may need the value of the ultimate
3498              * expression statement as the script's result, despite the fact
3499              * that it appears useless to the compiler.
3500              */
3501 266451             useful = wantval = !cx->fp->fun ||
3502                                cx->fp->fun->native ||
3503                                (cx->fp->flags & JSFRAME_SPECIAL);
3504 266451             if (!useful) {
3505 41480                 if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
3506 0                     return JS_FALSE;
3507             }
3508 266451             if (!useful) {
3509 0                 CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno;
3510 0                 if (!js_ReportCompileErrorNumber(cx, NULL, cg,
3511                                                  JSREPORT_WARNING |
3512                                                  JSREPORT_STRICT,
3513                                                  JSMSG_USELESS_EXPR)) {
3514 0                     return JS_FALSE;
3515                 }
3516             } else {
3517 266451                 if (!js_EmitTree(cx, cg, pn2))
3518 0                     return JS_FALSE;
3519 266451                 if (js_Emit1(cx, cg, wantval ? JSOP_POPV : JSOP_POP) < 0)
3520 0                     return JS_FALSE;
3521             }
3522         }
3523 0         break;
3524
3525       case TOK_COLON:
3526         /* Emit an annotated nop so we know to decompile a label. */
3527 0         atom = pn->pn_atom;
3528 0         ale = js_IndexAtom(cx, atom, &cg->atomList);
3529 0         if (!ale)
3530 0             return JS_FALSE;
3531 0         pn2 = pn->pn_expr;
3532 0         noteIndex = js_NewSrcNote2(cx, cg,
3533                                    (pn2->pn_type == TOK_LC)
3534                                    ? SRC_LABELBRACE
3535                                    : SRC_LABEL,
3536                                    (ptrdiff_t) ALE_INDEX(ale));
3537 0         if (noteIndex < 0 ||
3538             js_Emit1(cx, cg, JSOP_NOP) < 0) {
3539 0             return JS_FALSE;
3540         }
3541
3542         /* Emit code for the labeled statement. */
3543 0         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL,
3544                          CG_OFFSET(cg));
3545 0         stmtInfo.label = atom;
3546 0         if (!js_EmitTree(cx, cg, pn2))
3547 0             return JS_FALSE;
3548 0         if (!js_PopStatementCG(cx, cg))
3549 0             return JS_FALSE;
3550
3551         /* If the statement was compound, emit a note for the end brace. */
3552 0         if (pn2->pn_type == TOK_LC) {
3553 0             if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
3554                 js_Emit1(cx, cg, JSOP_NOP) < 0) {
3555 0                 return JS_FALSE;
3556             }
3557         }
3558 0         break;
3559
3560       case TOK_COMMA:
3561         /*
3562          * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
3563          * These notes help the decompiler bracket the bytecodes generated
3564          * from each sub-expression that follows a comma.
3565          */
3566 0         off = noteIndex = -1;
3567 0         for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
3568 0             if (!js_EmitTree(cx, cg, pn2))
3569 0                 return JS_FALSE;
3570 0             tmp = CG_OFFSET(cg);
3571 0             if (noteIndex >= 0) {
3572 0                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
3573 0                     return JS_FALSE;
3574             }
3575 0             if (!pn2->pn_next)
3576 0                 break;
3577 0             off = tmp;
3578 0             noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
3579 0             if (noteIndex < 0 ||
3580                 js_Emit1(cx, cg, JSOP_POP) < 0) {
3581 0                 return JS_FALSE;
3582             }
3583         }
3584 8220         break;
3585
3586       case TOK_ASSIGN:
3587         /*
3588          * Check left operand type and generate specialized code for it.
3589          * Specialize to avoid ECMA "reference type" values on the operand
3590          * stack, which impose pervasive runtime "GetValue" costs.
3591          */
3592 8220         pn2 = pn->pn_left;
3593 8220         JS_ASSERT(pn2->pn_type != TOK_RP);
3594 8220         atomIndex = (jsatomid) -1; /* Suppress warning. */
3595 8220         switch (pn2->pn_type) {
3596           case TOK_NAME:
3597 5500             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3598 0                 return JS_FALSE;
3599 5500             if (pn2->pn_slot >= 0) {
3600 2176                 atomIndex = (jsatomid) pn2->pn_slot;
3601             } else {
3602 3324                 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3603 3324                 if (!ale)
3604 0                     return JS_FALSE;
3605 3324                 atomIndex = ALE_INDEX(ale);
3606 3324                 EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
3607             }
3608 340             break;
3609           case TOK_DOT:
3610 340             if (!js_EmitTree(cx, cg, pn2->pn_expr))
3611 0                 return JS_FALSE;
3612 340             ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3613 340             if (!ale)
3614 0                 return JS_FALSE;
3615 340             atomIndex = ALE_INDEX(ale);
3616 340             break;
3617           case TOK_LB:
3618 2380             JS_ASSERT(pn->pn_arity == PN_BINARY);
3619 2380             if (!js_EmitTree(cx, cg, pn2->pn_left))
3620 0                 return JS_FALSE;
3621 2380             if (!js_EmitTree(cx, cg, pn2->pn_right))
3622 0                 return JS_FALSE;
3623 0             break;
3624 #if JS_HAS_LVALUE_RETURN
3625           case TOK_LP:
3626 0             if (!js_EmitTree(cx, cg, pn2))
3627 0                 return JS_FALSE;
3628 8220             break;
3629 #endif
3630           default:
3631 8220             JS_ASSERT(0);
3632         }
3633
3634 8220         op = pn->pn_op;
3635 #if JS_HAS_GETTER_SETTER
3636 8220         if (op == JSOP_GETTER || op == JSOP_SETTER) {
3637             /* We'll emit these prefix bytecodes after emitting the r.h.s. */
3638         } else
3639 #endif
3640         /* If += or similar, dup the left operand and get its value. */
3641 8220         if (op != JSOP_NOP) {
3642 17             switch (pn2->pn_type) {
3643               case TOK_NAME:
3644 17                 if (pn2->pn_op != JSOP_SETNAME) {
3645 0                     EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETARG)
3646                                        ? JSOP_GETARG
3647                                        : JSOP_GETVAR,
3648                                        atomIndex);
3649 17                     break;
3650                 }
3651                 /* FALL THROUGH */
3652               case TOK_DOT:
3653 17                 if (js_Emit1(cx, cg, JSOP_DUP) < 0)
3654 0                     return JS_FALSE;
3655 17                 EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex);
3656 0                 break;
3657               case TOK_LB:
3658 #if JS_HAS_LVALUE_RETURN
3659               case TOK_LP:
3660 #endif
3661 0                 if (js_Emit1(cx, cg, JSOP_DUP2) < 0)
3662 0                     return JS_FALSE;
3663 0                 if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
3664 0                     return JS_FALSE;
3665 8220                 break;
3666               default:;
3667             }
3668         }
3669
3670         /* Now emit the right operand (it may affect the namespace). */
3671 8220         if (!js_EmitTree(cx, cg, pn->pn_right))
3672 0             return JS_FALSE;
3673
3674         /* If += etc., emit the binary operator with a decompiler note. */
3675 8220         if (op != JSOP_NOP) {
3676 17             if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 ||
3677                 js_Emit1(cx, cg, op) < 0) {
3678 0                 return JS_FALSE;
3679             }
3680         }
3681
3682         /* Left parts such as a.b.c and a[b].c need a decompiler note. */
3683 8220         if (pn2->pn_type != TOK_NAME) {
3684 2720             if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
3685 0                 return JS_FALSE;
3686         }
3687
3688         /* Finally, emit the specialized assignment bytecode. */
3689 8220         switch (pn2->pn_type) {
3690           case TOK_NAME:
3691 5500             if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) {
3692           case TOK_DOT:
3693 5840                 EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
3694             }
3695 2380             break;
3696           case TOK_LB:
3697 #if JS_HAS_LVALUE_RETURN
3698           case TOK_LP:
3699 #endif
3700 2380             if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
3701 0                 return JS_FALSE;
3702 0             break;
3703           default:;
3704         }
3705 0         break;
3706
3707       case TOK_HOOK:
3708         /* Emit the condition, then branch if false to the else part. */
3709 0         if (!js_EmitTree(cx, cg, pn->pn_kid1))
3710 0             return JS_FALSE;
3711 0         noteIndex = js_NewSrcNote(cx, cg, SRC_COND);
3712 0         if (noteIndex < 0)
3713 0             return JS_FALSE;
3714 0         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
3715 0         if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2))
3716 0             return JS_FALSE;
3717
3718         /* Jump around else, fixup the branch, emit else, fixup jump. */
3719 0         jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
3720 0         if (jmp < 0)
3721 0             return JS_FALSE;
3722 0         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
3723 0         if (!js_EmitTree(cx, cg, pn->pn_kid3))
3724 0             return JS_FALSE;
3725 0         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
3726 0         if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
3727 0             return JS_FALSE;
3728
3729         /*
3730          * Because each branch pushes a single value, but our stack budgeting
3731          * analysis ignores branches, we now have two values accounted for in
3732          * cg->stackDepth.  Execution will follow only one path, so we must
3733          * decrement cg->stackDepth here.  Failing to do this will foil code,
3734          * such as the try/catch/finally exception handling code generator,
3735          * that samples cg->stackDepth for use at runtime (JSOP_SETSP).
3736          */
3737 0         JS_ASSERT(cg->stackDepth > 1);
3738 0         cg->stackDepth--;
3739 0         break;
3740
3741       case TOK_OR:
3742       case TOK_AND:
3743         /*
3744          * JSOP_OR converts the operand on the stack to boolean, and if true,
3745          * leaves the original operand value on the stack and jumps; otherwise
3746          * it pops and falls into the next bytecode, which evaluates the right
3747          * operand.  The jump goes around the right operand evaluation.
3748          *
3749          * JSOP_AND converts the operand on the stack to boolean, and if false,
3750          * leaves the original operand value on the stack and jumps; otherwise
3751          * it pops and falls into the right operand's bytecode.
3752          *
3753          * Avoid tail recursion for long ||...|| expressions and long &&...&&
3754          * expressions or long mixtures of ||'s and &&'s that can easily blow
3755          * the stack, by forward-linking and then backpatching all the JSOP_OR
3756          * and JSOP_AND bytecodes' immediate jump-offset operands.
3757          */
3758 2882         pn3 = pn;
3759 2882         if (!js_EmitTree(cx, cg, pn->pn_left))
3760 0             return JS_FALSE;
3761 2882         top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
3762 2882         if (top < 0)
3763 0             return JS_FALSE;
3764 2882         jmp = top;
3765 2882         pn2 = pn->pn_right;
3766 3701         while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) {
3767 819             pn = pn2;
3768 819             if (!js_EmitTree(cx, cg, pn->pn_left))
3769 0                 return JS_FALSE;
3770 819             off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
3771 819             if (off < 0)
3772 0                 return JS_FALSE;
3773 819             if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp))
3774 0                 return JS_FALSE;
3775 819             jmp = off;
3776 819             pn2 = pn->pn_right;
3777         }
3778 2882         if (!js_EmitTree(cx, cg, pn2))
3779 0             return JS_FALSE;
3780 2882         off = CG_OFFSET(cg);
3781 3701         do {
3782 3701             pc = CG_CODE(cg, top);
3783 3701             tmp = GetJumpOffset(cg, pc);
3784 3701             CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
3785 3701             *pc = pn3->pn_op;
3786 3701             top += tmp;
3787 3701         } while ((pn3 = pn3->pn_right) != pn2);
3788 21755         break;
3789
3790       case TOK_BITOR:
3791       case TOK_BITXOR:
3792       case TOK_BITAND:
3793       case TOK_EQOP:
3794       case TOK_RELOP:
3795 #if JS_HAS_IN_OPERATOR
3796       case TOK_IN:
3797 #endif
3798 #if JS_HAS_INSTANCEOF
3799       case TOK_INSTANCEOF:
3800 #endif
3801       case TOK_SHOP:
3802       case TOK_PLUS:
3803       case TOK_MINUS:
3804       case TOK_STAR:
3805       case TOK_DIVOP:
3806 21755         if (pn->pn_arity == PN_LIST) {
3807             /* Left-associative operator chain: avoid too much recursion. */
3808 1038             pn2 = pn->pn_head;
3809 1038             if (!js_EmitTree(cx, cg, pn2))
3810 0                 return JS_FALSE;
3811 1038             op = pn->pn_op;
3812 3216             while ((pn2 = pn2->pn_next) != NULL) {
3813 2178                 if (!js_EmitTree(cx, cg, pn2))
3814 0                     return JS_FALSE;
3815 2178                 if (js_Emit1(cx, cg, op) < 0)
3816 0                     return JS_FALSE;
3817             }
3818         } else {
3819             /* Binary operators that evaluate both operands unconditionally. */
3820 20717             if (!js_EmitTree(cx, cg, pn->pn_left))
3821 0                 return JS_FALSE;
3822 20717             if (!js_EmitTree(cx, cg, pn->pn_right))
3823 0                 return JS_FALSE;
3824 20717             if (js_Emit1(cx, cg, pn->pn_op) < 0)
3825 0                 return JS_FALSE;
3826         }
3827 412         break;
3828
3829 #if JS_HAS_EXCEPTIONS
3830       case TOK_THROW:
3831 #endif
3832       case TOK_UNARYOP:
3833         /* Unary op, including unary +/-. */
3834 412         if (!js_EmitTree(cx, cg, pn->pn_kid))
3835 0             return JS_FALSE;
3836 412         if (js_Emit1(cx, cg, pn->pn_op) < 0)
3837 0             return JS_FALSE;
3838 442         break;
3839
3840       case TOK_INC:
3841       case TOK_DEC:
3842         /* Emit lvalue-specialized code for ++/-- operators. */
3843 442         pn2 = pn->pn_kid;
3844 442         JS_ASSERT(pn2->pn_type != TOK_RP);
3845 442         op = pn->pn_op;
3846 442         switch (pn2->pn_type) {
3847           case TOK_NAME:
3848 136             pn2->pn_op = op;
3849 136             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3850 0                 return JS_FALSE;
3851 136             op = pn2->pn_op;
3852 136             if (pn2->pn_slot >= 0) {
3853 136                 if (pn2->pn_attrs & JSPROP_READONLY)
3854 0                     op = JSOP_GETVAR;
3855 136                 atomIndex = (jsatomid) pn2->pn_slot;
3856 136                 EMIT_ATOM_INDEX_OP(op, atomIndex);
3857             } else {
3858 0                 if (!EmitAtomOp(cx, pn2, op, cg))
3859 0                     return JS_FALSE;
3860             }
3861 0             break;
3862           case TOK_DOT:
3863 0             if (!EmitPropOp(cx, pn2, op, cg))
3864 0                 return JS_FALSE;
3865 306             break;
3866           case TOK_LB:
3867 306             if (!EmitElemOp(cx, pn2, op, cg))
3868 0                 return JS_FALSE;
3869 0             break;
3870 #if JS_HAS_LVALUE_RETURN
3871           case TOK_LP:
3872 0             if (!js_EmitTree(cx, cg, pn2))
3873 0                 return JS_FALSE;
3874 0             if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
3875                                CG_OFFSET(cg) - pn2->pn_offset) < 0) {
3876 0                 return JS_FALSE;
3877             }
3878 0             if (js_Emit1(cx, cg, op) < 0)
3879 0                 return JS_FALSE;
3880 0             break;
3881 #endif
3882           default:
3883 0             JS_ASSERT(0);
3884         }
3885 0         break;
3886
3887       case TOK_DELETE:
3888         /* Under ECMA 3, deleting a non-reference returns true. */
3889 0         pn2 = pn->pn_kid;
3890 0         switch (pn2->pn_type) {
3891           case TOK_NAME:
3892 0             pn2->pn_op = JSOP_DELNAME;
3893 0             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3894 0                 return JS_FALSE;
3895 0             op = pn2->pn_op;
3896 0             if (op == JSOP_FALSE) {
3897 0                 if (js_Emit1(cx, cg, op) < 0)
3898 0                     return JS_FALSE;
3899             } else {
3900 0                 if (!EmitAtomOp(cx, pn2, op, cg))
3901 0                     return JS_FALSE;
3902             }
3903 0             break;
3904           case TOK_DOT:
3905 0             if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))
3906 0                 return JS_FALSE;
3907 0             break;
3908           case TOK_LB:
3909 0             if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))
3910 0                 return JS_FALSE;
3911 0             break;
3912           default:
3913 0             if (js_Emit1(cx, cg, JSOP_TRUE) < 0)
3914 0                 return JS_FALSE;
3915         }
3916 29623         break;
3917
3918       case TOK_DOT:
3919         /*
3920          * Pop a stack operand, convert it to object, get a property named by
3921          * this bytecode's immediate-indexed atom operand, and push its value
3922          * (not a reference to it).  This bytecode sets the virtual machine's
3923          * "obj" register to the left operand's ToObject conversion result,
3924          * for use by JSOP_PUSHOBJ.
3925          */
3926 29623         ok = EmitPropOp(cx, pn, pn->pn_op, cg);
3927 29623         break;
3928
3929       case TOK_LB:
3930         /*
3931          * Pop two operands, convert the left one to object and the right one
3932          * to property name (atom or tagged int), get the named property, and
3933          * push its value.  Set the "obj" register to the result of ToObject
3934          * on the left operand.
3935          */
3936 18487         ok = EmitElemOp(cx, pn, pn->pn_op, cg);
3937 18487         break;
3938
3939       case TOK_NEW:
3940       case TOK_LP:
3941         /*
3942          * Emit function call or operator new (constructor call) code.
3943          * First, emit code for the left operand to evaluate the callable or
3944          * constructable object expression.
3945          */
3946 270828         pn2 = pn->pn_head;
3947 270828         if (!js_EmitTree(cx, cg, pn2))
3948 0             return JS_FALSE;
3949
3950         /* Remember start of callable-object bytecode for decompilation hint. */
3951 270828         off = pn2->pn_offset;
3952
3953         /*
3954          * Push the virtual machine's "obj" register, which was set by a name,
3955          * property, or element get (or set) bytecode.
3956          */
3957 270828         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
3958 0             return JS_FALSE;
3959
3960         /*
3961          * Emit code for each argument in order, then emit the JSOP_*CALL or
3962          * JSOP_NEW bytecode with a two-byte immediate telling how many args
3963          * were pushed on the operand stack.
3964          */
3965 555612         for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
3966 284784             if (!js_EmitTree(cx, cg, pn2))
3967 0                 return JS_FALSE;
3968         }
3969 270828         if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)
3970 0             return JS_FALSE;
3971 270828         argc = pn->pn_count - 1;
3972 270828         if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
3973 0             return JS_FALSE;
3974 5303         break;
3975
3976 #if JS_HAS_INITIALIZERS
3977       case TOK_RB:
3978         /*
3979          * Emit code for [a, b, c] of the form:
3980          *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
3981          * but use a stack slot for t and avoid dup'ing and popping it via
3982          * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
3983          */
3984 5303         ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom,
3985                            &cg->atomList);
3986 5303         if (!ale)
3987 0             return JS_FALSE;
3988 5303         EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
3989 5303         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
3990 0             return JS_FALSE;
3991 5303         if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
3992 0             return JS_FALSE;
3993
3994 5303         pn2 = pn->pn_head;
3995 #if JS_HAS_SHARP_VARS
3996 5303         if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
3997 0             EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
3998 0             pn2 = pn2->pn_next;
3999         }
4000 #endif
4001
4002 28843         for (atomIndex = 0; pn2; pn2 = pn2->pn_next) {
4003             /* PrimaryExpr enforced ATOM_INDEX_LIMIT, so in-line optimize. */
4004 23540             JS_ASSERT(atomIndex < ATOM_INDEX_LIMIT);
4005 23540             if (atomIndex == 0) {
4006 5303                 if (js_Emit1(cx, cg, JSOP_ZERO) < 0)
4007 0                     return JS_FALSE;
4008 18237             } else if (atomIndex == 1) {
4009 5303                 if (js_Emit1(cx, cg, JSOP_ONE) < 0)
4010 0                     return JS_FALSE;
4011             } else {
4012 12934                 EMIT_ATOM_INDEX_OP(JSOP_UINT16, (jsatomid)atomIndex);
4013             }
4014
4015             /* Sub-optimal: holes in a sparse initializer are void-filled. */
4016 23540             if (pn2->pn_type == TOK_COMMA) {
4017 0                 if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
4018 0                     return JS_FALSE;
4019             } else {
4020 23540                 if (!js_EmitTree(cx, cg, pn2))
4021 0                     return JS_FALSE;
4022             }
4023 23540             if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
4024 0                 return JS_FALSE;
4025
4026 23540             atomIndex++;
4027         }
4028
4029 5303         if (pn->pn_extra) {
4030             /* Emit a source note so we know to decompile an extra comma. */
4031 0             if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
4032 0                 return JS_FALSE;
4033         }
4034
4035         /* Emit an op for sharp array cleanup and decompilation. */
4036 5303         if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
4037 0             return JS_FALSE;
4038 0         break;
4039
4040       case TOK_RC:
4041         /*
4042          * Emit code for {p:a, '%q':b, 2:c} of the form:
4043          *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;
4044          * but use a stack slot for t and avoid dup'ing and popping it via
4045          * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
4046          */
4047 0         ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,
4048                            &cg->atomList);
4049 0         if (!ale)
4050 0             return JS_FALSE;
4051 0         EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
4052
4053 0         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
4054 0             return JS_FALSE;
4055 0         if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
4056 0             return JS_FALSE;
4057
4058 0         pn2 = pn->pn_head;
4059 #if JS_HAS_SHARP_VARS
4060 0         if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
4061 0             EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
4062 0             pn2 = pn2->pn_next;
4063         }
4064 #endif
4065
4066 0         for (; pn2; pn2 = pn2->pn_next) {
4067             /* Emit an index for t[2], else map an atom for t.p or t['%q']. */
4068 0             pn3 = pn2->pn_left;
4069 0             switch (pn3->pn_type) {
4070               case TOK_NUMBER:
4071 0                 if (!EmitNumberOp(cx, pn3->pn_dval, cg))
4072 0                     return JS_FALSE;
4073 0                 break;
4074               case TOK_NAME:
4075               case TOK_STRING:
4076 0                 ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
4077 0                 if (!ale)
4078 0                     return JS_FALSE;
4079 0                 break;
4080               default:
4081 0                 JS_ASSERT(0);
4082             }
4083
4084             /* Emit code for the property initializer. */
4085 0             if (!js_EmitTree(cx, cg, pn2->pn_right))
4086 0                 return JS_FALSE;
4087
4088 #if JS_HAS_GETTER_SETTER
4089 0             op = pn2->pn_op;
4090 0             if (op == JSOP_GETTER || op == JSOP_SETTER) {
4091 0                 if (js_Emit1(cx, cg, op) < 0)
4092 0                     return JS_FALSE;
4093             }
4094 #endif
4095             /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
4096 0             if (pn3->pn_type == TOK_NUMBER) {
4097 0                 if (js_NewSrcNote(cx, cg, SRC_LABEL) < 0)
4098 0                     return JS_FALSE;
4099 0                 if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
4100 0                     return JS_FALSE;
4101             } else {
4102 0                 EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
4103             }
4104         }
4105
4106         /* Emit an op for sharpArray cleanup and decompilation. */
4107 0         if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
4108 0             return JS_FALSE;
4109 0         break;
4110
4111 #if JS_HAS_SHARP_VARS
4112       case TOK_DEFSHARP:
4113 0         if (!js_EmitTree(cx, cg, pn->pn_kid))
4114 0             return JS_FALSE;
4115 0         EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);
4116 0         break;
4117
4118       case TOK_USESHARP:
4119 0         EMIT_ATOM_INDEX_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
4120 340         break;
4121 #endif /* JS_HAS_SHARP_VARS */
4122 #endif /* JS_HAS_INITIALIZERS */
4123
4124       case TOK_RP:
4125         /*
4126          * The node for (e) has e as its kid, enabling users who want to nest
4127          * assignment expressions in conditions to avoid the error correction
4128          * done by Condition (from x = y to x == y) by double-parenthesizing.
4129          */
4130 340         if (!js_EmitTree(cx, cg, pn->pn_kid))
4131 0             return JS_FALSE;
4132 340         if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
4133 0             return JS_FALSE;
4134 483655         break;
4135
4136       case TOK_NAME:
4137 483655         if (!LookupArgOrVar(cx, &cg->treeContext, pn))
4138 0             return JS_FALSE;
4139 483655         op = pn->pn_op;
4140 483655         if (op == JSOP_ARGUMENTS) {
4141 0             if (js_Emit1(cx, cg, op) < 0)
4142 0                 return JS_FALSE;
4143 483655             break;
4144         }
4145 483655         if (pn->pn_slot >= 0) {
4146 49980             atomIndex = (jsatomid) pn->pn_slot;
4147 49980             EMIT_ATOM_INDEX_OP(op, atomIndex);
4148 576757             break;
4149         }
4150         /* FALL THROUGH */
4151       case TOK_STRING:
4152       case TOK_OBJECT:
4153         /*
4154          * The scanner and parser associate JSOP_NAME with TOK_NAME, although
4155          * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME,
4156          * JSOP_FORNAME, etc.).  Among JSOP_*NAME* variants, only JSOP_NAME
4157          * may generate the first operand of a call or new expression, so only
4158          * it sets the "obj" virtual machine register to the object along the
4159          * scope chain in which the name was found.
4160          *
4161          * Token types for STRING and OBJECT have corresponding bytecode ops
4162          * in pn_op and emit the same format as NAME, so they share this code.
4163          */
4164 576757         ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
4165 576757         break;
4166
4167       case TOK_NUMBER:
4168 16349         ok = EmitNumberOp(cx, pn->pn_dval, cg);
4169 16349         break;
4170
4171       case TOK_PRIMARY:
4172 2215         if (js_Emit1(cx, cg, pn->pn_op) < 0)
4173 0             return JS_FALSE;
4174 0         break;
4175
4176 #if JS_HAS_DEBUGGER_KEYWORD
4177       case TOK_DEBUGGER:
4178 0         if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0)
4179 0             return JS_FALSE;
4180 1320700         break;
4181 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4182
4183       default:
4184 1320700         JS_ASSERT(0);
4185     }
4186
4187 1320700     if (ok && --cg->emitLevel == 0 && cg->spanDeps)
4188 0         ok = OptimizeSpanDeps(cx, cg);
4189
4190 1320700     return ok;
4191 }
4192
4193 JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
4194     {"null",            0,      0,      0},
4195     {"if",              0,      0,      0},
4196     {"if-else",         1,      0,      1},
4197     {"while",           1,      0,      1},
4198     {"for",             3,      1,      1},
4199     {"continue",        0,      0,      0},
4200     {"var",             0,      0,      0},
4201     {"pcdelta",         1,      0,      1},
4202     {"assignop",        0,      0,      0},
4203     {"cond",            1,      0,      1},
4204     {"reserved0",       0,      0,      0},
4205     {"hidden",          0,      0,      0},
4206     {"pcbase",          1,      0,     -1},
4207     {"label",           1,      0,      0},
4208     {"labelbrace",      1,      0,      0},
4209     {"endbrace",        0,      0,      0},
4210     {"break2label",     1,      0,      0},
4211     {"cont2label",      1,      0,      0},
4212     {"switch",          2,      0,      1},
4213     {"funcdef",         1,      0,      0},
4214     {"catch",           1,     11,      1},
4215     {"const",           0,      0,      0},
4216     {"newline",         0,      0,      0},
4217     {"setline",         1,      0,      0},
4218     {"xdelta",          0,      0,      0},
4219 };
4220
4221 static intN
4222 AllocSrcNote(JSContext *cx, JSCodeGenerator *cg)
4223 1350312 {
4224 1350312     intN index;
4225 1350312     JSArenaPool *pool;
4226 1350312     size_t size;
4227
4228 1350312     index = CG_NOTE_COUNT(cg);
4229 1350312     if (((uintN)index & CG_NOTE_MASK(cg)) == 0) {
4230 11761         pool = cg->notePool;
4231 11761         size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
4232 11761         if (!CG_NOTES(cg)) {
4233             /* Allocate the first note array lazily; leave noteMask alone. */
4234 5126             JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size);
4235         } else {
4236             /* Grow by doubling note array size; update noteMask on success. */
4237 6635             JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
4238 6635             if (CG_NOTES(cg))
4239 6635                 CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
4240         }
4241 11761         if (!CG_NOTES(cg)) {
4242 0             JS_ReportOutOfMemory(cx);
4243 0             return -1;
4244         }
4245     }
4246
4247 1350312     CG_NOTE_COUNT(cg) = index + 1;
4248 1350312     return index;
4249 }
4250
4251 intN
4252 js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
4253 1320912 {
4254 1320912     intN index, n;
4255 1320912     jssrcnote *sn;
4256 1320912     ptrdiff_t offset, delta, xdelta;
4257
4258     /*
4259      * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then
4260      * incrementing CG_NOTE_COUNT(cg).
4261      */
4262 1320912     index = AllocSrcNote(cx, cg);
4263 1320912     if (index < 0)
4264 0         return -1;
4265 1320912     sn = &CG_NOTES(cg)[index];
4266
4267     /*
4268      * Compute delta from the last annotated bytecode's offset.  If it's too
4269      * big to fit in sn, allocate one or more xdelta notes and reset sn.
4270      */
4271 1320912     offset = CG_OFFSET(cg);
4272 1320912     delta = offset - CG_LAST_NOTE_OFFSET(cg);
4273 1320912     CG_LAST_NOTE_OFFSET(cg) = offset;
4274 1320912     if (delta >= SN_DELTA_LIMIT) {
4275 29400         do {
4276 29400             xdelta = JS_MIN(delta, SN_XDELTA_MASK);
4277 29400             SN_MAKE_XDELTA(sn, xdelta);
4278 29400             delta -= xdelta;
4279 29400             index = AllocSrcNote(cx, cg);
4280 29400             if (index < 0)
4281 0                 return -1;
4282 29400             sn = &CG_NOTES(cg)[index];
4283 29400         } while (delta >= SN_DELTA_LIMIT);
4284     }
4285
4286     /*
4287      * Initialize type and delta, then allocate the minimum number of notes
4288      * needed for type's arity.  Usually, we won't need more, but if an offset
4289      * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg).
4290      */
4291 1320912     SN_MAKE_NOTE(sn, type, delta);
4292 1729975     for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) {
4293 409063         if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
4294 0             return -1;
4295     }
4296 1320912     return index;
4297 }
4298
4299 intN
4300 js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
4301                ptrdiff_t offset)
4302 401717 {
4303 401717     intN index;
4304
4305 401717     index = js_NewSrcNote(cx, cg, type);
4306 401717     if (index >= 0) {
4307 401717         if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
4308 0             return -1;
4309     }
4310 401717     return index;
4311 }
4312
4313 intN
4314 js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
4315                ptrdiff_t offset1, ptrdiff_t offset2)
4316 0 {
4317 0     intN index;
4318
4319 0     index = js_NewSrcNote(cx, cg, type);
4320 0     if (index >= 0) {
4321 0         if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1))
4322 0             return -1;
4323 0         if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2))
4324 0             return -1;
4325     }
4326 0     return index;
4327 }
4328
4329 static JSBool
4330 GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg)
4331 204 {
4332 204     JSArenaPool *pool;
4333 204     size_t size;
4334
4335     /* Grow by doubling note array size; update noteMask on success. */
4336 204     pool = cg->notePool;
4337 204     size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
4338 204     JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
4339 204     if (!CG_NOTES(cg)) {
4340 0         JS_ReportOutOfMemory(cx);
4341 0         return JS_FALSE;
4342     }
4343 204     CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
4344 204     return JS_TRUE;
4345 }
4346
4347 jssrcnote *
4348 js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
4349                      ptrdiff_t delta)
4350 204 {
4351 204     ptrdiff_t base, limit, newdelta, diff;
4352 204     intN index;
4353
4354     /*
4355      * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to
4356      * main script note deltas, and only by a small positive amount.
4357      */
4358 204     JS_ASSERT(cg->current == &cg->main);
4359 204     JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
4360
4361 204     base = SN_DELTA(sn);
4362 204     limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
4363 204     newdelta = base + delta;
4364 204     if (newdelta < limit) {
4365 68         SN_SET_DELTA(sn, newdelta);
4366     } else {
4367 136         index = sn - cg->main.notes;
4368 136         if ((cg->main.noteCount & cg->main.noteMask) == 0) {
4369 0             if (!GrowSrcNotes(cx, cg))
4370 0                 return NULL;
4371 0             sn = cg->main.notes + index;
4372         }
4373 136         diff = cg->main.noteCount - index;
4374 136         cg->main.noteCount++;
4375 136         memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
4376 136         SN_MAKE_XDELTA(sn, delta);
4377 136         sn++;
4378     }
4379 204     return sn;
4380 }
4381
4382 uintN
4383 js_SrcNoteLength(jssrcnote *sn)
4384 0 {
4385 0     uintN arity;
4386 0     jssrcnote *base;
4387
4388 0     arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity;
4389 0     for (base = sn++; arity; sn++, arity--) {
4390 0         if (*sn & SN_3BYTE_OFFSET_FLAG)
4391 0             sn += 2;
4392     }
4393 0     return sn - base;
4394 }
4395
4396 JS_FRIEND_API(ptrdiff_t)
4397 js_GetSrcNoteOffset(jssrcnote *sn, uintN which)
4398 0 {
4399     /* Find the offset numbered which (i.e., skip exactly which offsets). */
4400 0     JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
4401 0     JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
4402 0     for (sn++; which; sn++, which--) {
4403 0         if (*sn & SN_3BYTE_OFFSET_FLAG)
4404 0             sn += 2;
4405     }
4406 0     if (*sn & SN_3BYTE_OFFSET_FLAG) {
4407 0         return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
4408                            | (sn[1] << 8)
4409                            | sn[2]);
4410     }
4411 0     return (ptrdiff_t)*sn;
4412 }
4413
4414 JSBool
4415 js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
4416                     uintN which, ptrdiff_t offset)
4417 409120 {
4418 409120     jssrcnote *sn;
4419 409120     ptrdiff_t diff;
4420
4421 409120     if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) {
4422 0         ReportStatementTooLarge(cx, cg);
4423 0         return JS_FALSE;
4424     }
4425
4426     /* Find the offset numbered which (i.e., skip exactly which offsets). */
4427 409120     sn = &CG_NOTES(cg)[index];
4428 409120     JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
4429 409120     JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
4430 409324     for (sn++; which; sn++, which--) {
4431 204         if (*sn & SN_3BYTE_OFFSET_FLAG)
4432 0             sn += 2;
4433     }
4434
4435     /* See if the new offset requires three bytes. */
4436 409120     if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
4437         /* Maybe this offset was already set to a three-byte value. */
4438 12031         if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
4439             /* Losing, need to insert another two bytes for this offset. */
4440 12031             index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote);
4441
4442             /*
4443              * Simultaneously test to see if the source note array must grow to
4444              * accomodate either the first or second byte of additional storage
4445              * required by this 3-byte offset.
4446              */
4447 12031             if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {
4448 204                 if (!GrowSrcNotes(cx, cg))
4449 0                     return JS_FALSE;
4450 204                 sn = CG_NOTES(cg) + index;
4451             }
4452 12031             CG_NOTE_COUNT(cg) += 2;
4453
4454 12031             diff = CG_NOTE_COUNT(cg) - (index + 3);
4455 12031             JS_ASSERT(diff >= 0);
4456 12031             if (diff > 0)
4457 1720                 memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
4458         }
4459 12031         *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
4460 12031         *sn++ = (jssrcnote)(offset >> 8);
4461     }
4462 409120     *sn = (jssrcnote)offset;
4463 409120     return JS_TRUE;
4464 }
4465
4466 #ifdef DEBUG_brendan
4467 #define NBINS 10
4468 static uint32 hist[NBINS];
4469
4470 void DumpSrcNoteSizeHist()
4471 {
4472     static FILE *fp;
4473     int i, n;
4474
4475     if (!fp) {
4476         fp = fopen("/tmp/srcnotes.hist", "w");
4477         if (!fp)
4478             return;
4479         setlinebuf(fp);
4480     }
4481     fprintf(fp, "SrcNote size histogram:\n");
4482     for (i = 0; i < NBINS; i++) {
4483         fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]);
4484         for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n)
4485             fputc('*', fp);
4486         fputc('\n', fp);
4487     }
4488     fputc('\n', fp);
4489 }
4490 #endif
4491
4492 /*
4493  * Fill in the storage at notes with prolog and main srcnotes; the space at
4494  * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h.
4495  * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's
4496  * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES!
4497  */
4498 JSBool
4499 js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes)
4500 4920 {
4501 4920     uintN prologCount, mainCount, totalCount;
4502 4920     ptrdiff_t offset, delta;
4503 4920     jssrcnote *sn;
4504
4505 4920     JS_ASSERT(cg->current == &cg->main);
4506
4507 4920     prologCount = cg->prolog.noteCount;
4508 4920     if (prologCount && cg->prolog.currentLine != cg->firstLine) {
4509 376         CG_SWITCH_TO_PROLOG(cg);
4510 376         if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0)
4511 0             return JS_FALSE;
4512 376         prologCount = cg->prolog.noteCount;
4513 376         CG_SWITCH_TO_MAIN(cg);
4514     } else {
4515         /*
4516          * Either no prolog srcnotes, or no line number change over prolog.
4517          * We don't need a SRC_SETLINE, but we may need to adjust the offset
4518          * of the first main note, by adding to its delta and possibly even
4519          * prepending SRC_XDELTA notes to it to account for prolog bytecodes
4520          * that came at and after the last annotated bytecode.
4521          */
4522 4544         offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset;
4523 4544         JS_ASSERT(offset >= 0);
4524 4544         if (offset > 0) {
4525             /* NB: Use as much of the first main note's delta as we can. */
4526 68             sn = cg->main.notes;
4527 68             delta = SN_IS_XDELTA(sn)
4528                     ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
4529                     : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
4530 68             if (offset < delta)
4531 0                 delta = offset;
4532 340             for (;;) {
4533 204                 if (!js_AddToSrcNoteDelta(cx, cg, sn, delta))
4534 0                     return JS_FALSE;
4535 204                 offset -= delta;
4536 204                 if (offset == 0)
4537 68                     break;
4538 136                 delta = JS_MIN(offset, SN_XDELTA_MASK);
4539 136                 sn = cg->main.notes;
4540             }
4541         }
4542     }
4543
4544 4920     mainCount = cg->main.noteCount;
4545 4920     totalCount = prologCount + mainCount;
4546 4920     if (prologCount)
4547 376         memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount));
4548 4920     memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
4549 4920     SN_MAKE_TERMINATOR(&notes[totalCount]);
4550
4551 #ifdef DEBUG_brendan
4552   { int bin = JS_CeilingLog2(totalCount);
4553     if (bin >= NBINS)
4554         bin = NBINS - 1;
4555     ++hist[bin];
4556   }
4557 #endif
4558 4920     return JS_TRUE;
4559 }
4560
4561 JSBool
4562 js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
4563 199024 {
4564 199024     size_t size, incr;
4565 199024     ptrdiff_t delta;
4566
4567 199024     size = TRYNOTE_SIZE(cg->treeContext.tryCount);
4568 199024     if (size <= cg->tryNoteSpace)
4569 199024         return JS_TRUE;
4570
4571     /*
4572      * Allocate trynotes from cx->tempPool.
4573      * XXX Too much growing and we bloat, as other tempPool allocators block
4574      * in-place growth, and we never recycle old free space in an arena.
4575      * YYY But once we consume an entire arena, we'll realloc it, letting the
4576      * malloc heap recycle old space, while still freeing _en masse_ via the
4577      * arena pool.
4578      */
4579 0     if (!cg->tryBase) {
4580 0         size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK));
4581 0         JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);
4582 0         if (!cg->tryBase)
4583 0             return JS_FALSE;
4584 0         cg->tryNoteSpace = size;
4585 0         cg->tryNext = cg->tryBase;
4586     } else {
4587 0         delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char);
4588 0         incr = size - cg->tryNoteSpace;
4589 0         incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK));
4590 0         size = cg->tryNoteSpace;
4591 0         JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr);
4592 0         if (!cg->tryBase)
4593 0             return JS_FALSE;
4594 0         cg->tryNoteSpace = size + incr;
4595 0         cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta);
4596     }
4597 0     return JS_TRUE;
4598 }
4599
4600 JSTryNote *
4601 js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
4602               ptrdiff_t end, ptrdiff_t catchStart)
4603 0 {
4604 0     JSTryNote *tn;
4605
4606 0     JS_ASSERT(cg->tryBase <= cg->tryNext);
4607 0     JS_ASSERT(catchStart >= 0);
4608 0     tn = cg->tryNext++;
4609 0     tn->start = start;
4610 0     tn->length = end - start;
4611 0     tn->catchStart = catchStart;
4612 0     return tn;
4613 }
4614
4615 void
4616 js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
4617 0 {
4618 0     uintN count;
4619
4620 0     count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote);
4621 0     if (!count)
4622 0         return;
4623
4624 0     memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count));
4625 0     notes[count].start = 0;
4626 0     notes[count].length = CG_OFFSET(cg);
4627 0     notes[count].catchStart = 0;