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 0 {
82 0     memset(cg, 0, sizeof *cg);
83 0     TREE_CONTEXT_INIT(&cg->treeContext);
84 0     cg->treeContext.flags |= TCF_COMPILING;
85 0     cg->codePool = codePool;
86 0     cg->notePool = notePool;
87 0     cg->codeMark = JS_ARENA_MARK(codePool);
88 0     cg->noteMark = JS_ARENA_MARK(notePool);
89 0     cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
90 0     cg->current = &cg->main;
91 0     cg->filename = filename;
92 0     cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno;
93 0     cg->principals = principals;
94 0     ATOM_LIST_INIT(&cg->atomList);
95 0     cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1;
96 0     ATOM_LIST_INIT(&cg->constList);
97 0     return JS_TRUE;
98 }
99
100 JS_FRIEND_API(void)
101 js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
102 0 {
103 0     TREE_CONTEXT_FINISH(&cg->treeContext);
104 0     JS_ARENA_RELEASE(cg->codePool, cg->codeMark);
105 0     JS_ARENA_RELEASE(cg->notePool, cg->noteMark);
106 0     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 0 {
112 0     jsbytecode *base, *limit, *next;
113 0     ptrdiff_t offset, length;
114 0     size_t incr, size;
115
116 0     base = CG_BASE(cg);
117 0     next = CG_NEXT(cg);
118 0     limit = CG_LIMIT(cg);
119 0     offset = PTRDIFF(next, base, jsbytecode);
120 0     if (next + delta > limit) {
121 0         length = offset + delta;
122 0         length = (length <= BYTECODE_CHUNK)
123                  ? BYTECODE_CHUNK
124                  : JS_BIT(JS_CeilingLog2(length));
125 0         incr = BYTECODE_SIZE(length);
126 0         if (!base) {
127 0             JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
128         } else {
129 0             size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
130 0             incr -= size;
131 0             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
132         }
133 0         if (!base) {
134 0             JS_ReportOutOfMemory(cx);
135 0             return -1;
136         }
137 0         CG_BASE(cg) = base;
138 0         CG_LIMIT(cg) = base + length;
139 0         CG_NEXT(cg) = base + offset;
140     }
141 0     return offset;
142 }
143
144 static void
145 UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
146 0 {
147 0     jsbytecode *pc;
148 0     const JSCodeSpec *cs;
149 0     intN nuses;
150
151 0     pc = CG_CODE(cg, target);
152 0     cs = &js_CodeSpec[pc[0]];
153 0     nuses = cs->nuses;
154 0     if (nuses < 0)
155 0         nuses = 2 + GET_ARGC(pc);       /* stack: fun, this, [argc arguments] */
156 0     cg->stackDepth -= nuses;
157 0     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 0     cg->stackDepth += cs->ndefs;
165 0     if ((uintN)cg->stackDepth > cg->maxStackDepth)
166 0         cg->maxStackDepth = cg->stackDepth;
167 }
168
169 ptrdiff_t
170 js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
171 0 {
172 0     ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
173
174 0     if (offset >= 0) {
175 0         *CG_NEXT(cg)++ = (jsbytecode)op;
176 0         UpdateDepth(cx, cg, offset);
177     }
178 0     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 0 {
200 0     ptrdiff_t offset = EmitCheck(cx, cg, op, 3);
201
202 0     if (offset >= 0) {
203 0         jsbytecode *next = CG_NEXT(cg);
204 0         next[0] = (jsbytecode)op;
205 0         next[1] = op1;
206 0         next[2] = op2;
207 0         CG_NEXT(cg) = next + 3;
208 0         UpdateDepth(cx, cg, offset);
209     }
210 0     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 0 {
648 0     JSSpanDep *sd;
649
650 0     JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
651 0     if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) {
652 0         SET_JUMP_OFFSET(pc, delta);
653 0         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 0 {
1133 0     ptrdiff_t jmp;
1134 0     jsbytecode *pc;
1135
1136 0     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 0     jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off));
1142 0     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 0     return jmp;
1148 }
1149
1150 static ptrdiff_t
1151 GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc)
1152 0 {
1153 0     JSSpanDep *sd;
1154 0     JSJumpTarget *jt;
1155 0     ptrdiff_t top;
1156
1157 0     if (!cg->spanDeps)
1158 0         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 0 {
1176 0     if (!cg->spanDeps) {
1177 0         if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) {
1178 0             SET_JUMP_OFFSET(pc, off);
1179 0             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 0 {
1192 0     JSStmtInfo *stmt;
1193
1194 0     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1195 0         if (stmt->type == STMT_WITH)
1196 0             return JS_TRUE;
1197     }
1198 0     return JS_FALSE;
1199 }
1200
1201 JSBool
1202 js_InCatchBlock(JSTreeContext *tc, JSAtom *atom)
1203 0 {
1204 0     JSStmtInfo *stmt;
1205
1206 0     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1207 0         if (stmt->type == STMT_CATCH && stmt->label == atom)
1208 0             return JS_TRUE;
1209     }
1210 0     return JS_FALSE;
1211 }
1212
1213 void
1214 js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
1215                  ptrdiff_t top)
1216 0 {
1217 0     stmt->type = type;
1218 0     SET_STATEMENT_TOP(stmt, top);
1219 0     stmt->label = NULL;
1220 0     stmt->down = tc->topStmt;
1221 0     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 0 {
1243 0     intN depth;
1244 0     JSStmtInfo *stmt;
1245 0     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 0     if (returnop) {
1263 0         JS_ASSERT(*returnop == JSOP_RETURN);
1264 0         for (stmt = cg->treeContext.topStmt; stmt != toStmt;
1265              stmt = stmt->down) {
1266 0             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 0         if (*returnop == JSOP_RETURN)
1280 0             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 0     depth = cg->stackDepth;
1290 0     for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
1291 0         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 0             break;
1325
1326           default:;
1327         }
1328     }
1329
1330 0     cg->stackDepth = depth;
1331 0     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 0 {
1338 0     intN index;
1339 0     ptrdiff_t jmp;
1340
1341 0     if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
1342 0         return -1;
1343
1344 0     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 0     } else if (noteType != SRC_NULL) {
1353 0         if (js_NewSrcNote(cx, cg, noteType) < 0)
1354 0             return -1;
1355     }
1356
1357 0     EMIT_BACKPATCH_OP(cx, cg, *last, JSOP_BACKPATCH, jmp);
1358 0     return jmp;
1359 }
1360
1361 static JSBool
1362 BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
1363           jsbytecode *target, jsbytecode op)
1364 0 {
1365 0     jsbytecode *pc, *stop;
1366 0     ptrdiff_t delta, span;
1367
1368 0     pc = CG_CODE(cg, last);
1369 0     stop = CG_CODE(cg, -1);
1370 0     while (pc != stop) {
1371 0         delta = GetJumpOffset(cg, pc);
1372 0         span = PTRDIFF(target, pc, jsbytecode);
1373 0         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 0         *pc = op;
1381 0         pc -= delta;
1382     }
1383 0     return JS_TRUE;
1384 }
1385
1386 void
1387 js_PopStatement(JSTreeContext *tc)
1388 0 {
1389 0     tc->topStmt = tc->topStmt->down;
1390 }
1391
1392 JSBool
1393 js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
1394 0 {
1395 0     JSStmtInfo *stmt;
1396
1397 0     stmt = cg->treeContext.topStmt;
1398 0     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 0     js_PopStatement(&cg->treeContext);
1404 0     return JS_TRUE;
1405 }
1406
1407 JSBool
1408 js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
1409                              JSParseNode *pn)
1410 0 {
1411 0     jsdouble dval;
1412 0     jsint ival;
1413 0     JSAtom *valueAtom;
1414 0     JSAtomListElement *ale;
1415
1416     /* XXX just do numbers for now */
1417 0     if (pn->pn_type == TOK_NUMBER) {
1418 0         dval = pn->pn_dval;
1419 0         valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival))
1420                     ? js_AtomizeInt(cx, ival, 0)
1421                     : js_AtomizeDouble(cx, dval, 0);
1422 0         if (!valueAtom)
1423 0             return JS_FALSE;
1424 0         ale = js_IndexAtom(cx, atom, &cg->constList);
1425 0         if (!ale)
1426 0             return JS_FALSE;
1427 0         ALE_SET_VALUE(ale, ATOM_KEY(valueAtom));
1428     }
1429 0     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 0 {
1505 0     JSAtomListElement *ale;
1506
1507 0     ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
1508 0     if (!ale)
1509 0         return JS_FALSE;
1510 0     EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));
1511 0     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 0 {
1531 0     JSObject *obj, *pobj;
1532 0     JSClass *clasp;
1533 0     JSAtom *atom;
1534 0     JSScopeProperty *sprop;
1535 0     JSOp op;
1536
1537 0     JS_ASSERT(pn->pn_type == TOK_NAME);
1538 0     if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
1539 0         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 0     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 0     obj = cx->fp->varobj;
1554 0     clasp = OBJ_GET_CLASS(cx, obj);
1555 0     if (clasp != &js_FunctionClass && clasp != &js_CallClass)
1556 0         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 0     atom = pn->pn_atom;
1564 0     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 0     if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop))
1578 0         return JS_FALSE;
1579 0     op = pn->pn_op;
1580 0     if (sprop) {
1581 0         if (pobj == obj) {
1582 0             JSPropertyOp getter = sprop->getter;
1583
1584 0             if (getter == js_GetArgument) {
1585 0                 switch (op) {
1586 0                   case JSOP_NAME:     op = JSOP_GETARG; break;
1587 0                   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 0                   default: JS_ASSERT(0);
1595                 }
1596 0             } else if (getter == js_GetLocalVariable ||
1597                        getter == js_GetCallVariable)
1598             {
1599 0                 switch (op) {
1600 0                   case JSOP_NAME:     op = JSOP_GETVAR; break;
1601 0                   case JSOP_SETNAME:  op = JSOP_SETVAR; break;
1602 0                   case JSOP_SETCONST: op = JSOP_SETVAR; break;
1603 0                   case JSOP_INCNAME:  op = JSOP_INCVAR; break;
1604 0                   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 0                   case JSOP_FORNAME:  op = JSOP_FORVAR; break;
1608 0                   case JSOP_DELNAME:  op = JSOP_FALSE; break;
1609 0                   default: JS_ASSERT(0);
1610                 }
1611             }
1612 0             if (op != pn->pn_op) {
1613 0                 pn->pn_op = op;
1614 0                 pn->pn_slot = sprop->shortid;
1615             }
1616 0             pn->pn_attrs = sprop->attrs;
1617         }
1618 0         OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
1619     }
1620
1621 0     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 0         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 0         tc->flags |= TCF_FUN_USES_NONLOCALS;
1636     }
1637 0     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 0 {
1656 0     JSBool ok;
1657 0     JSFunction *fun;
1658 0     JSParseNode *pn2;
1659
1660 0     ok = JS_TRUE;
1661 0     if (!pn || *answer)
1662 0         return ok;
1663
1664 0     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 0         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 0             *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 0         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 0             *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 0         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 0             *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 0     return ok;
1780 }
1781
1782 static JSBool
1783 EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
1784 0 {
1785 0     JSParseNode *pn2, *pndot, *pnup, *pndown;
1786 0     ptrdiff_t top;
1787 0     JSAtomListElement *ale;
1788
1789 0     pn2 = pn->pn_expr;
1790 0     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 0         if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
1795 0             return JS_FALSE;
1796 0         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 0     if (pn2->pn_type == TOK_DOT) {
1808 0         pndot = pn2;
1809 0         pnup = NULL;
1810 0         top = CG_OFFSET(cg);
1811 0         for (;;) {
1812             /* Reverse pndot->pn_expr to point up, not down. */
1813 0             pndot->pn_offset = top;
1814 0             pndown = pndot->pn_expr;
1815 0             pndot->pn_expr = pnup;
1816 0             if (pndown->pn_type != TOK_DOT)
1817 0                 break;
1818 0             pnup = pndot;
1819 0             pndot = pndown;
1820         }
1821
1822         /* pndown is a primary expression, not a dotted property reference. */
1823 0         if (!js_EmitTree(cx, cg, pndown))
1824 0             return JS_FALSE;
1825
1826 0         do {
1827             /* Walk back up the list, emitting annotated name ops. */
1828 0             if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
1829                                CG_OFFSET(cg) - pndown->pn_offset) < 0) {
1830 0                 return JS_FALSE;
1831             }
1832 0             ale = js_IndexAtom(cx, pndot->pn_atom, &cg->atomList);
1833 0             if (!ale)
1834 0                 return JS_FALSE;
1835 0             EMIT_ATOM_INDEX_OP(pndot->pn_op, ALE_INDEX(ale));
1836
1837             /* Reverse the pn_expr link again. */
1838 0             pnup = pndot->pn_expr;
1839 0             pndot->pn_expr = pndown;
1840 0             pndown = pndot;
1841 0         } while ((pndot = pnup) != NULL);
1842     } else {
1843 0         if (!js_EmitTree(cx, cg, pn2))
1844 0             return JS_FALSE;
1845     }
1846
1847 0     if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - pn2->pn_offset) < 0)
1848 0         return JS_FALSE;
1849 0     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 0         ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
1855 0         if (!ale)
1856 0             return JS_FALSE;
1857 0         EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));
1858     }
1859 0     return JS_TRUE;
1860 }
1861
1862 static JSBool
1863 EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
1864 0 {
1865 0     ptrdiff_t top;
1866 0     JSParseNode *left, *right, *next;
1867 0     jsint slot;
1868
1869 0     top = CG_OFFSET(cg);
1870 0     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 0         JS_ASSERT(pn->pn_arity == PN_BINARY);
1919 0         left = pn->pn_left;
1920 0         right = pn->pn_right;
1921
1922         /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */
1923 0         if (op == JSOP_GETELEM &&
1924             left->pn_type == TOK_NAME &&
1925             right->pn_type == TOK_NUMBER) {
1926 0             if (!LookupArgOrVar(cx, &cg->treeContext, left))
1927 0                 return JS_FALSE;
1928 0             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 0         if (!js_EmitTree(cx, cg, left))
1938 0             return JS_FALSE;
1939     }
1940 0     if (!js_EmitTree(cx, cg, right))
1941 0         return JS_FALSE;
1942 0     if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
1943 0         return JS_FALSE;
1944 0     return js_Emit1(cx, cg, op) >= 0;
1945 }
1946
1947 static JSBool
1948 EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
1949 0 {
1950 0     jsint ival;
1951 0     jsatomid atomIndex;
1952 0     JSAtom *atom;
1953 0     JSAtomListElement *ale;
1954
1955 0     if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
1956 0         if (ival == 0)
1957 0             return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
1958 0         if (ival == 1)
1959 0             return js_Emit1(cx, cg, JSOP_ONE) >= 0;
1960 0         if ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) {
1961 0             atomIndex = (jsatomid)ival;
1962 0             EMIT_ATOM_INDEX_OP(JSOP_UINT16, atomIndex);
1963 0             return JS_TRUE;
1964         }
1965 0         atom = js_AtomizeInt(cx, ival, 0);
1966     } else {
1967 0         atom = js_AtomizeDouble(cx, dval, 0);
1968     }
1969 0     if (!atom)
1970 0         return JS_FALSE;
1971 0     ale = js_IndexAtom(cx, atom, &cg->atomList);
1972 0     if (!ale)
1973 0         return JS_FALSE;
1974 0     EMIT_ATOM_INDEX_OP(JSOP_NUMBER, ALE_INDEX(ale));
1975 0     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 0 {
2418 0     JSStackFrame *fp, frame;
2419 0     JSObject *funobj;
2420 0     JSBool ok;
2421
2422 0     if (!js_AllocTryNotes(cx, cg))
2423 0         return JS_FALSE;
2424
2425 0     fp = cx->fp;
2426 0     funobj = fun->object;
2427 0     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
2428                       fp->scopeChain != funobj));
2429 0     memset(&frame, 0, sizeof frame);
2430 0     frame.fun = fun;
2431 0     frame.varobj = frame.scopeChain = funobj;
2432 0     frame.down = fp;
2433 0     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
2434                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
2435                   : JSFRAME_COMPILING;
2436 0     cx->fp = &frame;
2437 0     ok = js_EmitTree(cx, cg, body);
2438 0     cx->fp = fp;
2439 0     if (!ok)
2440 0         return JS_FALSE;
2441
2442 0     fun->script = js_NewScriptFromCG(cx, cg, fun);
2443 0     if (!fun->script)
2444 0         return JS_FALSE;
2445 0     if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
2446 0         fun->flags |= JSFUN_HEAVYWEIGHT;
2447 0     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 0 {
2484 0     UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
2485 0     return JS_TRUE;
2486 }
2487
2488 JSBool
2489 js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
2490 0 {
2491 0     JSBool ok, useful, wantval;
2492 0     JSStmtInfo *stmt, stmtInfo;
2493 0     ptrdiff_t top, off, tmp, beq, jmp;
2494 0     JSParseNode *pn2, *pn3;
2495 0     JSAtom *atom;
2496 0     JSAtomListElement *ale;
2497 0     jsatomid atomIndex;
2498 0     intN noteIndex;
2499 0     JSSrcNoteType noteType;
2500 0     jsbytecode *pc;
2501 0     JSOp op;
2502 0     uint32 argc;
2503 0     int stackDummy;
2504
2505 0     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 0     ok = JS_TRUE;
2511 0     cg->emitLevel++;
2512 0     pn->pn_offset = top = CG_OFFSET(cg);
2513
2514     /* Emit notes to tell the current bytecode's source line number. */
2515 0     UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
2516
2517 0     switch (pn->pn_type) {
2518       case TOK_FUNCTION:
2519       {
2520 0         void *cg2mark;
2521 0         JSCodeGenerator *cg2;
2522 0         JSFunction *fun;
2523
2524         /* Generate code for the function's body. */
2525 0         cg2mark = JS_ARENA_MARK(&cx->tempPool);
2526 0         JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool);
2527 0         if (!cg2) {
2528 0             JS_ReportOutOfMemory(cx);
2529 0             return JS_FALSE;
2530         }
2531 0         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 0         cg2->treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION;
2537 0         cg2->treeContext.tryCount = pn->pn_tryCount;
2538 0         cg2->parent = cg;
2539 0         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
2540 0         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 0         if (cg2->treeContext.flags &
2548             (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {
2549 0             cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
2550         }
2551 0         js_FinishCodeGenerator(cx, cg2);
2552 0         JS_ARENA_RELEASE(&cx->tempPool, cg2mark);
2553
2554         /* Make the function object a literal in the outer script's pool. */
2555 0         ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList);
2556 0         if (!ale)
2557 0             return JS_FALSE;
2558 0         atomIndex = ALE_INDEX(ale);
2559
2560 #if JS_HAS_LEXICAL_CLOSURE
2561         /* Emit a bytecode pointing to the closure object in its immediate. */
2562 0         if (pn->pn_op != JSOP_NOP) {
2563 0             EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
2564 0             break;
2565         }
2566 #endif
2567
2568         /* Top-level named functions need a nop for decompilation. */
2569 0         noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);
2570 0         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 0         CG_SWITCH_TO_PROLOG(cg);
2580
2581 #if JS_HAS_LEXICAL_CLOSURE
2582 0         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 0             EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
2607
2608 0         CG_SWITCH_TO_MAIN(cg);
2609 0         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 0         stmtInfo.type = STMT_IF;
2652 0         beq = jmp = -1;
2653 0         noteIndex = -1;
2654
2655       if_again:
2656         /* Emit code for the condition before pushing stmtInfo. */
2657 0         if (!js_EmitTree(cx, cg, pn->pn_kid1))
2658 0             return JS_FALSE;
2659 0         if (stmtInfo.type == STMT_IF) {
2660 0             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 0             JS_ASSERT(stmtInfo.type == STMT_ELSE);
2670 0             stmtInfo.type = STMT_IF;
2671 0             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 0         pn3 = pn->pn_kid3;
2677 0         noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
2678 0         if (noteIndex < 0)
2679 0             return JS_FALSE;
2680 0         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2681 0         if (beq < 0)
2682 0             return JS_FALSE;
2683
2684         /* Emit code for the then and optional else parts. */
2685 0         if (!js_EmitTree(cx, cg, pn->pn_kid2))
2686 0             return JS_FALSE;
2687 0         if (pn3) {
2688             /* Modify stmtInfo so we know we're in the else part. */
2689 0             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 0             jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL);
2698 0             if (jmp < 0)
2699 0                 return JS_FALSE;
2700
2701             /* Ensure the branch-if-false comes here, then emit the else. */
2702 0             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2703 0             if (pn3->pn_type == TOK_IF) {
2704 0                 pn = pn3;
2705 0                 goto if_again;
2706             }
2707
2708 0             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 0             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 0             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2723         }
2724 0         ok = js_PopStatementCG(cx, cg);
2725 0         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 0         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
2736 0         if (!js_EmitTree(cx, cg, pn->pn_left))
2737 0             return JS_FALSE;
2738 0         noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
2739 0         if (noteIndex < 0)
2740 0             return JS_FALSE;
2741 0         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2742 0         if (beq < 0)
2743 0             return JS_FALSE;
2744 0         if (!js_EmitTree(cx, cg, pn->pn_right))
2745 0             return JS_FALSE;
2746 0         jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
2747 0         if (jmp < 0)
2748 0             return JS_FALSE;
2749 0         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2750 0         if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
2751 0             return JS_FALSE;
2752 0         ok = js_PopStatementCG(cx, cg);
2753 0         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 0         beq = 0;                /* suppress gcc warnings */
2793 0         pn2 = pn->pn_left;
2794 0         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);
2795
2796 0         if (pn2->pn_type == TOK_IN) {
2797             /* Set stmtInfo type for later testing. */
2798 0             stmtInfo.type = STMT_FOR_IN_LOOP;
2799 0             noteIndex = -1;
2800
2801             /* If the left part is var x = i, bind x, evaluate i, and pop. */
2802 0             pn3 = pn2->pn_left;
2803 0             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 0                 JS_ASSERT(pn3->pn_type == TOK_NAME);
2809             }
2810
2811             /* Emit a push to allocate the iterator. */
2812 0             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 0             if (!js_EmitTree(cx, cg, pn2->pn_right))
2817 0                 return JS_FALSE;
2818 0             if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0)
2819 0                 return JS_FALSE;
2820
2821 0             top = CG_OFFSET(cg);
2822 0             SET_STATEMENT_TOP(&stmtInfo, top);
2823
2824             /* Compile a JSOP_FOR* bytecode based on the left hand side. */
2825 0             switch (pn3->pn_type) {
2826               case TOK_VAR:
2827 0                 pn3 = pn3->pn_head;
2828 0                 if (js_NewSrcNote(cx, cg, SRC_VAR) < 0)
2829 0                     return JS_FALSE;
2830                 /* FALL THROUGH */
2831               case TOK_NAME:
2832 0                 pn3->pn_op = JSOP_FORNAME;
2833 0                 if (!LookupArgOrVar(cx, &cg->treeContext, pn3))
2834 0                     return JS_FALSE;
2835 0                 op = pn3->pn_op;
2836 0                 if (pn3->pn_slot >= 0) {
2837 0                     if (pn3->pn_attrs & JSPROP_READONLY)
2838 0                         op = JSOP_GETVAR;
2839 0                     atomIndex = (jsatomid) pn3->pn_slot;
2840 0                     EMIT_ATOM_INDEX_OP(op, atomIndex);
2841                 } else {
2842 0                     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 0                 break;
2880
2881               default:
2882 0                 JS_ASSERT(0);
2883             }
2884 0             if (pn3->pn_type != TOK_LB) {
2885                 /* Annotate so the decompiler can find the loop-closing jump. */
2886 0                 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
2887 0                 if (noteIndex < 0)
2888 0                     return JS_FALSE;
2889
2890                 /* Pop and test the loop condition generated by JSOP_FOR*. */
2891 0                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2892 0                 if (beq < 0)
2893 0                     return JS_FALSE;
2894             }
2895         } else {
2896 0             if (!pn2->pn_kid1) {
2897                 /* No initializer: emit an annotated nop for the decompiler. */
2898 0                 op = JSOP_NOP;
2899             } else {
2900 0                 if (!js_EmitTree(cx, cg, pn2->pn_kid1))
2901 0                     return JS_FALSE;
2902 0                 op = JSOP_POP;
2903             }
2904 0             noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
2905 0             if (noteIndex < 0 ||
2906                 js_Emit1(cx, cg, op) < 0) {
2907 0                 return JS_FALSE;
2908             }
2909
2910 0             top = CG_OFFSET(cg);
2911 0             SET_STATEMENT_TOP(&stmtInfo, top);
2912 0             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 0                 if (!js_EmitTree(cx, cg, pn2->pn_kid2))
2918 0                     return JS_FALSE;
2919 0                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
2920                                          CG_OFFSET(cg) - top)) {
2921 0                     return JS_FALSE;
2922                 }
2923 0                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
2924 0                 if (beq < 0)
2925 0                     return JS_FALSE;
2926             }
2927
2928             /* Set pn3 (used below) here to avoid spurious gcc warnings. */
2929 0             pn3 = pn2->pn_kid3;
2930         }
2931
2932         /* Emit code for the loop body. */
2933 0         if (!js_EmitTree(cx, cg, pn->pn_right))
2934 0             return JS_FALSE;
2935
2936 0         if (pn2->pn_type != TOK_IN) {
2937             /* Set the second note offset so we can find the update part. */
2938 0             JS_ASSERT(noteIndex != -1);
2939 0             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
2940                                      CG_OFFSET(cg) - top)) {
2941 0                 return JS_FALSE;
2942             }
2943
2944 0             if (pn3) {
2945                 /* Set loop and enclosing "update" offsets, for continue. */
2946 0                 stmt = &stmtInfo;
2947 0                 do {
2948 0                     stmt->update = CG_OFFSET(cg);
2949 0                 } while ((stmt = stmt->down) != NULL &&
2950                          stmt->type == STMT_LABEL);
2951
2952 0                 if (!js_EmitTree(cx, cg, pn3))
2953 0                     return JS_FALSE;
2954 0                 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 0                 off = (ptrdiff_t) pn->pn_pos.end.lineno;
2959 0                 if (CG_CURRENT_LINE(cg) != (uintN) off) {
2960 0                     if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
2961 0                         return JS_FALSE;
2962 0                     CG_CURRENT_LINE(cg) = (uintN) off;
2963                 }
2964             }
2965
2966             /* The third note offset helps us find the loop-closing jump. */
2967 0             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 0         jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
2975 0         if (jmp < 0)
2976 0             return JS_FALSE;
2977 0         if (beq > 0)
2978 0             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
2979 0         if (pn2->pn_type == TOK_IN) {
2980             /* Set the SRC_WHILE note offset so we can find the closing jump. */
2981 0             JS_ASSERT(noteIndex != -1);
2982 0             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 0         if (!js_PopStatementCG(cx, cg))
2988 0             return JS_FALSE;
2989
2990 0         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 0             if (pn3->pn_type != TOK_LB) {
2996 0                 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 0         break;
3010
3011       case TOK_BREAK:
3012 0         stmt = cg->treeContext.topStmt;
3013 0         atom = pn->pn_atom;
3014 0         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 0             ale = NULL;
3023 0             while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
3024 0                 stmt = stmt->down;
3025 0             noteType = SRC_NULL;
3026         }
3027
3028 0         if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0)
3029 0             return JS_FALSE;
3030 0         break;
3031
3032       case TOK_CONTINUE:
3033 0         stmt = cg->treeContext.topStmt;
3034 0         atom = pn->pn_atom;
3035 0         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 0             ale = NULL;
3050 0             while (!STMT_IS_LOOP(stmt))
3051 0                 stmt = stmt->down;
3052 0             noteType = SRC_CONTINUE;
3053         }
3054
3055 0         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 0         break;
3377       }
3378
3379 #endif /* JS_HAS_EXCEPTIONS */
3380
3381       case TOK_VAR:
3382 0         off = noteIndex = -1;
3383 0         for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
3384 0             JS_ASSERT(pn2->pn_type == TOK_NAME);
3385 0             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3386 0                 return JS_FALSE;
3387 0             op = pn2->pn_op;
3388 0             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 0                 if (pn2->pn_slot >= 0) {
3395 0                     atomIndex = (jsatomid) pn2->pn_slot;
3396                 } else {
3397 0                     ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3398 0                     if (!ale)
3399 0                         return JS_FALSE;
3400 0                     atomIndex = ALE_INDEX(ale);
3401
3402 0                     if (!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
3403                         (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)) {
3404                         /* Emit a prolog bytecode to predefine the variable. */
3405 0                         CG_SWITCH_TO_PROLOG(cg);
3406 0                         if (!UpdateLineNumberNotes(cx, cg, pn2))
3407 0                             return JS_FALSE;
3408 0                         EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
3409 0                         CG_SWITCH_TO_MAIN(cg);
3410                     }
3411                 }
3412 0                 if (pn2->pn_expr) {
3413 0                     if (op == JSOP_SETNAME)
3414 0                         EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
3415 0                     pn3 = pn2->pn_expr;
3416 0                     if (!js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
3417                                                       pn3)) {
3418 0                         return JS_FALSE;
3419                     }
3420 0                     if (!js_EmitTree(cx, cg, pn3))
3421 0                         return JS_FALSE;
3422                 }
3423             }
3424 0             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 0             if (op == JSOP_ARGUMENTS) {
3432 0                 if (js_Emit1(cx, cg, op) < 0)
3433 0                     return JS_FALSE;
3434             } else {
3435 0                 EMIT_ATOM_INDEX_OP(op, atomIndex);
3436             }
3437 0             tmp = CG_OFFSET(cg);
3438 0             if (noteIndex >= 0) {
3439 0                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
3440 0                     return JS_FALSE;
3441             }
3442 0             if (!pn2->pn_next)
3443 0                 break;
3444 0             off = tmp;
3445 0             noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
3446 0             if (noteIndex < 0 ||
3447                 js_Emit1(cx, cg, JSOP_POP) < 0) {
3448 0                 return JS_FALSE;
3449             }
3450         }
3451 0         if (pn->pn_extra) {
3452 0             if (js_Emit1(cx, cg, JSOP_POP) < 0)
3453 0                 return JS_FALSE;
3454         }
3455 0         break;
3456
3457       case TOK_RETURN:
3458         /* Push a return value */
3459 0         pn2 = pn->pn_kid;
3460 0         if (pn2) {
3461 0             if (!js_EmitTree(cx, cg, pn2))
3462 0                 return JS_FALSE;
3463         } else {
3464 0             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 0         op = JSOP_RETURN;
3477 0         if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
3478 0             return JS_FALSE;
3479 0         if (js_Emit1(cx, cg, op) < 0)
3480 0             return JS_FALSE;
3481 0         break;
3482
3483       case TOK_LC:
3484 0         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
3485 0         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3486 0             if (!js_EmitTree(cx, cg, pn2))
3487 0                 return JS_FALSE;
3488         }
3489 0         ok = js_PopStatementCG(cx, cg);
3490 0         break;
3491
3492       case TOK_SEMI:
3493 0         pn2 = pn->pn_kid;
3494 0         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 0             useful = wantval = !cx->fp->fun ||
3502                                cx->fp->fun->native ||
3503                                (cx->fp->flags & JSFRAME_SPECIAL);
3504 0             if (!useful) {
3505 0                 if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
3506 0                     return JS_FALSE;
3507             }
3508 0             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 0                 if (!js_EmitTree(cx, cg, pn2))
3518 0                     return JS_FALSE;
3519 0                 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 0         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 0         pn2 = pn->pn_left;
3593 0         JS_ASSERT(pn2->pn_type != TOK_RP);
3594 0         atomIndex = (jsatomid) -1; /* Suppress warning. */
3595 0         switch (pn2->pn_type) {
3596           case TOK_NAME:
3597 0             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3598 0                 return JS_FALSE;
3599 0             if (pn2->pn_slot >= 0) {
3600 0                 atomIndex = (jsatomid) pn2->pn_slot;
3601             } else {
3602 0                 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3603 0                 if (!ale)
3604 0                     return JS_FALSE;
3605 0                 atomIndex = ALE_INDEX(ale);
3606 0                 EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
3607             }
3608 0             break;
3609           case TOK_DOT:
3610 0             if (!js_EmitTree(cx, cg, pn2->pn_expr))
3611 0                 return JS_FALSE;
3612 0             ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3613 0             if (!ale)
3614 0                 return JS_FALSE;
3615 0             atomIndex = ALE_INDEX(ale);
3616 0             break;
3617           case TOK_LB:
3618 0             JS_ASSERT(pn->pn_arity == PN_BINARY);
3619 0             if (!js_EmitTree(cx, cg, pn2->pn_left))
3620 0                 return JS_FALSE;
3621 0             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 0             break;
3629 #endif
3630           default:
3631 0             JS_ASSERT(0);
3632         }
3633
3634 0         op = pn->pn_op;
3635 #if JS_HAS_GETTER_SETTER
3636 0         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 0         if (op != JSOP_NOP) {
3642 0             switch (pn2->pn_type) {
3643               case TOK_NAME:
3644 0                 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 0                     break;
3650                 }
3651                 /* FALL THROUGH */
3652               case TOK_DOT:
3653 0                 if (js_Emit1(cx, cg, JSOP_DUP) < 0)
3654 0                     return JS_FALSE;
3655 0                 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 0                 break;
3666               default:;
3667             }
3668         }
3669
3670         /* Now emit the right operand (it may affect the namespace). */
3671 0         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 0         if (op != JSOP_NOP) {
3676 0             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 0         if (pn2->pn_type != TOK_NAME) {
3684 0             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 0         switch (pn2->pn_type) {
3690           case TOK_NAME:
3691 0             if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) {
3692           case TOK_DOT:
3693 0                 EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
3694             }
3695 0             break;
3696           case TOK_LB:
3697 #if JS_HAS_LVALUE_RETURN
3698           case TOK_LP:
3699 #endif
3700 0             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 0         pn3 = pn;
3759 0         if (!js_EmitTree(cx, cg, pn->pn_left))
3760 0             return JS_FALSE;
3761 0         top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
3762 0         if (top < 0)
3763 0             return JS_FALSE;
3764 0         jmp = top;
3765 0         pn2 = pn->pn_right;
3766 0         while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) {
3767 0             pn = pn2;
3768 0             if (!js_EmitTree(cx, cg, pn->pn_left))
3769 0                 return JS_FALSE;
3770 0             off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
3771 0             if (off < 0)
3772 0                 return JS_FALSE;
3773 0             if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp))
3774 0                 return JS_FALSE;
3775 0             jmp = off;
3776 0             pn2 = pn->pn_right;
3777         }
3778 0         if (!js_EmitTree(cx, cg, pn2))
3779 0             return JS_FALSE;
3780 0         off = CG_OFFSET(cg);
3781 0         do {
3782 0             pc = CG_CODE(cg, top);
3783 0             tmp = GetJumpOffset(cg, pc);
3784 0             CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
3785 0             *pc = pn3->pn_op;
3786 0             top += tmp;
3787 0         } while ((pn3 = pn3->pn_right) != pn2);
3788 0         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 0         if (pn->pn_arity == PN_LIST) {
3807             /* Left-associative operator chain: avoid too much recursion. */
3808 0             pn2 = pn->pn_head;
3809 0             if (!js_EmitTree(cx, cg, pn2))
3810 0                 return JS_FALSE;
3811 0             op = pn->pn_op;
3812 0             while ((pn2 = pn2->pn_next) != NULL) {
3813 0                 if (!js_EmitTree(cx, cg, pn2))
3814 0                     return JS_FALSE;
3815 0                 if (js_Emit1(cx, cg, op) < 0)
3816 0                     return JS_FALSE;
3817             }
3818         } else {
3819             /* Binary operators that evaluate both operands unconditionally. */
3820 0             if (!js_EmitTree(cx, cg, pn->pn_left))
3821 0                 return JS_FALSE;
3822 0             if (!js_EmitTree(cx, cg, pn->pn_right))
3823 0                 return JS_FALSE;
3824 0             if (js_Emit1(cx, cg, pn->pn_op) < 0)
3825 0                 return JS_FALSE;
3826         }
3827 0         break;
3828
3829 #if JS_HAS_EXCEPTIONS
3830       case TOK_THROW:
3831 #endif
3832       case TOK_UNARYOP:
3833         /* Unary op, including unary +/-. */
3834 0         if (!js_EmitTree(cx, cg, pn->pn_kid))
3835 0             return JS_FALSE;
3836 0         if (js_Emit1(cx, cg, pn->pn_op) < 0)
3837 0             return JS_FALSE;
3838 0         break;
3839
3840       case TOK_INC:
3841       case TOK_DEC:
3842         /* Emit lvalue-specialized code for ++/-- operators. */
3843 0         pn2 = pn->pn_kid;
3844 0         JS_ASSERT(pn2->pn_type != TOK_RP);
3845 0         op = pn->pn_op;
3846 0         switch (pn2->pn_type) {
3847           case TOK_NAME:
3848 0             pn2->pn_op = op;
3849 0             if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3850 0                 return JS_FALSE;
3851 0             op = pn2->pn_op;
3852 0             if (pn2->pn_slot >= 0) {
3853 0                 if (pn2->pn_attrs & JSPROP_READONLY)
3854 0                     op = JSOP_GETVAR;
3855 0                 atomIndex = (jsatomid) pn2->pn_slot;
3856 0                 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 0             break;
3866           case TOK_LB:
3867 0             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 0         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 0         ok = EmitPropOp(cx, pn, pn->pn_op, cg);
3927 0         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 0         ok = EmitElemOp(cx, pn, pn->pn_op, cg);
3937 0         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 0         pn2 = pn->pn_head;
3947 0         if (!js_EmitTree(cx, cg, pn2))
3948 0             return JS_FALSE;
3949
3950         /* Remember start of callable-object bytecode for decompilation hint. */
3951 0         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 0         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 0         for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
3966 0             if (!js_EmitTree(cx, cg, pn2))
3967 0                 return JS_FALSE;
3968         }
3969 0         if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)
3970 0             return JS_FALSE;
3971 0         argc = pn->pn_count - 1;
3972 0         if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
3973 0             return JS_FALSE;
3974 0         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 0         ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom,
3985                            &cg->atomList);
3986 0         if (!ale)
3987 0             return JS_FALSE;
3988 0         EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
3989 0         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
3990 0             return JS_FALSE;
3991 0         if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
3992 0             return JS_FALSE;
3993
3994 0         pn2 = pn->pn_head;
3995 #if JS_HAS_SHARP_VARS
3996 0         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 0         for (atomIndex = 0; pn2; pn2 = pn2->pn_next) {
4003             /* PrimaryExpr enforced ATOM_INDEX_LIMIT, so in-line optimize. */
4004 0             JS_ASSERT(atomIndex < ATOM_INDEX_LIMIT);
4005 0             if (atomIndex == 0) {
4006 0                 if (js_Emit1(cx, cg, JSOP_ZERO) < 0)
4007 0                     return JS_FALSE;
4008 0             } else if (atomIndex == 1) {
4009 0                 if (js_Emit1(cx, cg, JSOP_ONE) < 0)
4010 0                     return JS_FALSE;
4011             } else {
4012 0                 EMIT_ATOM_INDEX_OP(JSOP_UINT16, (jsatomid)atomIndex);
4013             }
4014
4015             /* Sub-optimal: holes in a sparse initializer are void-filled. */
4016 0             if (pn2->pn_type == TOK_COMMA) {
4017 0                 if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
4018 0                     return JS_FALSE;
4019             } else {
4020 0                 if (!js_EmitTree(cx, cg, pn2))
4021 0                     return JS_FALSE;
4022             }
4023 0             if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
4024 0                 return JS_FALSE;
4025
4026 0             atomIndex++;
4027         }
4028
4029 0         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 0         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 0         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 0         if (!js_EmitTree(cx, cg, pn->pn_kid))
4131 0             return JS_FALSE;
4132 0         if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
4133 0             return JS_FALSE;
4134 0         break;
4135
4136       case TOK_NAME:
4137 0         if (!LookupArgOrVar(cx, &cg->treeContext, pn))
4138 0             return JS_FALSE;
4139 0         op = pn->pn_op;
4140 0         if (op == JSOP_ARGUMENTS) {
4141 0             if (js_Emit1(cx, cg, op) < 0)
4142 0                 return JS_FALSE;
4143 0             break;
4144         }
4145 0         if (pn->pn_slot >= 0) {
4146 0             atomIndex = (jsatomid) pn->pn_slot;
4147 0             EMIT_ATOM_INDEX_OP(op, atomIndex);
4148 0             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 0         ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
4165 0         break;
4166
4167       case TOK_NUMBER:
4168 0         ok = EmitNumberOp(cx, pn->pn_dval, cg);
4169 0         break;
4170
4171       case TOK_PRIMARY:
4172 0         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 0         break;
4181 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4182
4183       default:
4184 0         JS_ASSERT(0);
4185     }
4186
4187 0     if (ok && --cg->emitLevel == 0 && cg->spanDeps)
4188 0         ok = OptimizeSpanDeps(cx, cg);
4189
4190 0     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 0 {
4224 0     intN index;
4225 0     JSArenaPool *pool;
4226 0     size_t size;
4227
4228 0     index = CG_NOTE_COUNT(cg);
4229 0     if (((uintN)index & CG_NOTE_MASK(cg)) == 0) {
4230 0         pool = cg->notePool;
4231 0         size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
4232 0         if (!CG_NOTES(cg)) {
4233             /* Allocate the first note array lazily; leave noteMask alone. */
4234 0             JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size);
4235         } else {
4236             /* Grow by doubling note array size; update noteMask on success. */
4237 0             JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
4238 0             if (CG_NOTES(cg))
4239 0                 CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
4240         }
4241 0         if (!CG_NOTES(cg)) {
4242 0             JS_ReportOutOfMemory(cx);
4243 0             return -1;
4244         }
4245     }
4246
4247 0     CG_NOTE_COUNT(cg) = index + 1;
4248 0     return index;
4249 }
4250
4251 intN
4252 js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
4253 0 {
4254 0     intN index, n;
4255 0     jssrcnote *sn;
4256 0     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 0     index = AllocSrcNote(cx, cg);
4263 0     if (index < 0)
4264 0         return -1;
4265 0     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 0     offset = CG_OFFSET(cg);
4272 0     delta = offset - CG_LAST_NOTE_OFFSET(cg);
4273 0     CG_LAST_NOTE_OFFSET(cg) = offset;
4274 0     if (delta >= SN_DELTA_LIMIT) {
4275 0         do {
4276 0             xdelta = JS_MIN(delta, SN_XDELTA_MASK);
4277 0             SN_MAKE_XDELTA(sn, xdelta);
4278 0             delta -= xdelta;
4279 0             index = AllocSrcNote(cx, cg);
4280 0             if (index < 0)
4281 0                 return -1;
4282 0             sn = &CG_NOTES(cg)[index];
4283 0         } 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 0     SN_MAKE_NOTE(sn, type, delta);
4292 0     for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) {
4293 0         if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
4294 0             return -1;
4295     }
4296 0     return index;
4297 }
4298
4299 intN
4300 js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
4301                ptrdiff_t offset)
4302 0 {
4303 0     intN index;
4304
4305 0     index = js_NewSrcNote(cx, cg, type);
4306 0     if (index >= 0) {
4307 0         if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
4308 0             return -1;
4309     }
4310 0     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 0 {
4332 0     JSArenaPool *pool;
4333 0     size_t size;
4334
4335     /* Grow by doubling note array size; update noteMask on success. */
4336 0     pool = cg->notePool;
4337 0     size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
4338 0     JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
4339 0     if (!CG_NOTES(cg)) {
4340 0         JS_ReportOutOfMemory(cx);
4341 0         return JS_FALSE;
4342     }
4343 0     CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
4344 0     return JS_TRUE;
4345 }
4346
4347 jssrcnote *
4348 js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
4349                      ptrdiff_t delta)
4350 0 {
4351 0     ptrdiff_t base, limit, newdelta, diff;
4352 0     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 0     JS_ASSERT(cg->current == &cg->main);
4359 0     JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
4360
4361 0     base = SN_DELTA(sn);
4362 0     limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
4363 0     newdelta = base + delta;
4364 0     if (newdelta < limit) {
4365 0         SN_SET_DELTA(sn, newdelta);
4366     } else {
4367 0         index = sn - cg->main.notes;
4368 0         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 0         diff = cg->main.noteCount - index;
4374 0         cg->main.noteCount++;
4375 0         memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
4376 0         SN_MAKE_XDELTA(sn, delta);
4377 0         sn++;
4378     }
4379 0     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 0 {
4418 0     jssrcnote *sn;
4419 0     ptrdiff_t diff;
4420
4421 0     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 0     sn = &CG_NOTES(cg)[index];
4428 0     JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
4429 0     JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
4430 0     for (sn++; which; sn++, which--) {
4431 0         if (*sn & SN_3BYTE_OFFSET_FLAG)
4432 0             sn += 2;
4433     }
4434
4435     /* See if the new offset requires three bytes. */
4436 0     if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
4437         /* Maybe this offset was already set to a three-byte value. */
4438 0         if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
4439             /* Losing, need to insert another two bytes for this offset. */
4440 0             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 0             if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {
4448 0                 if (!GrowSrcNotes(cx, cg))
4449 0                     return JS_FALSE;
4450 0                 sn = CG_NOTES(cg) + index;
4451             }
4452 0             CG_NOTE_COUNT(cg) += 2;
4453
4454 0             diff = CG_NOTE_COUNT(cg) - (index + 3);
4455 0             JS_ASSERT(diff >= 0);
4456 0             if (diff > 0)
4457 0                 memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
4458         }
4459 0         *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
4460 0         *sn++ = (jssrcnote)(offset >> 8);
4461     }
4462 0     *sn = (jssrcnote)offset;
4463 0     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 0 {
4501 0     uintN prologCount, mainCount, totalCount;
4502 0     ptrdiff_t offset, delta;
4503 0     jssrcnote *sn;
4504
4505 0     JS_ASSERT(cg->current == &cg->main);
4506
4507 0     prologCount = cg->prolog.noteCount;
4508 0     if (prologCount && cg->prolog.currentLine != cg->firstLine) {
4509 0         CG_SWITCH_TO_PROLOG(cg);
4510 0         if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0)
4511 0             return JS_FALSE;
4512 0         prologCount = cg->prolog.noteCount;
4513 0         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 0         offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset;
4523 0         JS_ASSERT(offset >= 0);
4524 0         if (offset > 0) {
4525             /* NB: Use as much of the first main note's delta as we can. */
4526 0             sn = cg->main.notes;
4527 0             delta = SN_IS_XDELTA(sn)
4528                     ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
4529                     : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
4530 0             if (offset < delta)
4531 0                 delta = offset;
4532 0             for (;;) {
4533 0                 if (!js_AddToSrcNoteDelta(cx, cg, sn, delta))
4534 0                     return JS_FALSE;
4535 0                 offset -= delta;
4536 0                 if (offset == 0)
4537 0                     break;
4538 0                 delta = JS_MIN(offset, SN_XDELTA_MASK);
4539 0                 sn = cg->main.notes;
4540             }
4541         }
4542     }
4543
4544 0     mainCount = cg->main.noteCount;
4545 0     totalCount = prologCount + mainCount;
4546 0     if (prologCount)
4547 0         memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount));
4548 0     memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
4549 0     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 0     return JS_TRUE;
4559 }
4560
4561 JSBool
4562 js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
4563 0 {
4564 0     size_t size, incr;
4565 0     ptrdiff_t delta;
4566
4567 0     size = TRYNOTE_SIZE(cg->treeContext.tryCount);
4568 0     if (size <= cg->tryNoteSpace)
4569 0         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;