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