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 5000 {
84 5000 memset(cg, 0, sizeof *cg);
85 5000 TREE_CONTEXT_INIT(&cg->treeContext);
86 5000 cg->treeContext.flags |= TCF_COMPILING;
87 5000 cg->codePool = codePool;
88 5000 cg->notePool = notePool;
89 5000 cg->codeMark = JS_ARENA_MARK(codePool);
90 5000 cg->noteMark = JS_ARENA_MARK(notePool);
91 5000 cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
92 5000 cg->current = &cg->main;
93 5000 cg->filename = filename;
94 5000 cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno;
95 5000 cg->principals = principals;
96 5000 ATOM_LIST_INIT(&cg->atomList);
97 5000 cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1;
98 5000 ATOM_LIST_INIT(&cg->constList);
99 5000 return JS_TRUE;
100 }
101
102 JS_FRIEND_API(void)
103 js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
104 5000 {
105 TREE_CONTEXT_FINISH(&cg->treeContext);
106 5000 JS_ARENA_RELEASE(cg->codePool, cg->codeMark);
107 5000 JS_ARENA_RELEASE(cg->notePool, cg->noteMark);
108 5000 JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);
109 }
110
111 static ptrdiff_t
112 EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)
113 1677101 {
114 jsbytecode *base, *limit, *next;
115 ptrdiff_t offset, length;
116 size_t incr, size;
117
118 1677101 base = CG_BASE(cg);
119 1677101 next = CG_NEXT(cg);
120 1677101 limit = CG_LIMIT(cg);
121 1677101 offset = PTRDIFF(next, base, jsbytecode);
122 1677101 if (next + delta > limit) {
123 10195 length = offset + delta;
124 10195 length = (length <= BYTECODE_CHUNK)
125 ? BYTECODE_CHUNK
126 : JS_BIT(JS_CeilingLog2(length));
127 10195 incr = BYTECODE_SIZE(length);
128 10195 if (!base) {
129 5269 JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
130 } else {
131 4926 size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
132 4926 incr -= size;
133 4926 JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
134 }
135 10195 if (!base) {
136 0 JS_ReportOutOfMemory(cx);
137 0 return -1;
138 }
139 10195 CG_BASE(cg) = base;
140 10195 CG_LIMIT(cg) = base + length;
141 10195 CG_NEXT(cg) = base + offset;
142 }
143 1677101 return offset;
144 }
145
146 static void
147 UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
148 1677101 {
149 jsbytecode *pc;
150 const JSCodeSpec *cs;
151 intN nuses;
152
153 1677101 pc = CG_CODE(cg, target);
154 1677101 cs = &js_CodeSpec[pc[0]];
155 1677101 nuses = cs->nuses;
156 1677101 if (nuses < 0)
157 268145 nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */
158 1677101 cg->stackDepth -= nuses;
159 JS_ASSERT(cg->stackDepth >= 0);
160 1677101 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 1677101 cg->stackDepth += cs->ndefs;
170 1677101 if ((uintN)cg->stackDepth > cg->maxStackDepth)
171 23204 cg->maxStackDepth = cg->stackDepth;
172 }
173
174 ptrdiff_t
175 js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
176 664412 {
177 664412 ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
178
179 664412 if (offset >= 0) {
180 664412 *CG_NEXT(cg)++ = (jsbytecode)op;
181 664412 UpdateDepth(cx, cg, offset);
182 }
183 664412 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 1012689 {
205 1012689 ptrdiff_t offset = EmitCheck(cx, cg, op, 3);
206
207 1012689 if (offset >= 0) {
208 1012689 jsbytecode *next = CG_NEXT(cg);
209 1012689 next[0] = (jsbytecode)op;
210 1012689 next[1] = op1;
211 1012689 next[2] = op2;
212 1012689 CG_NEXT(cg) = next + 3;
213 1012689 UpdateDepth(cx, cg, offset);
214 }
215 1012689 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 }
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 0 if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) {
458 0 ReportStatementTooLarge(cx, cg);
459 0 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 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 786 {
654 JSSpanDep *sd;
655
656 JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
657 786 if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) {
658 786 SET_JUMP_OFFSET(pc, delta);
659 786 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 }
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 }
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 28459 {
1141 JSBool extend;
1142 ptrdiff_t jmp;
1143 jsbytecode *pc;
1144
1145 28459 extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off;
1146 28459 if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg))
1147 0 return JS_FALSE;
1148
1149 28459 jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off));
1150 28459 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 28459 return jmp;
1156 }
1157
1158 static ptrdiff_t
1159 GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc)
1160 10763 {
1161 JSSpanDep *sd;
1162 JSJumpTarget *jt;
1163 ptrdiff_t top;
1164
1165 10763 if (!cg->spanDeps)
1166 10763 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 26661 {
1184 26661 if (!cg->spanDeps) {
1185 26661 if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) {
1186 26661 SET_JUMP_OFFSET(pc, off);
1187 26661 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 122842 {
1200 JSStmtInfo *stmt;
1201
1202 514165 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1203 391323 if (stmt->type == STMT_WITH)
1204 0 return JS_TRUE;
1205 }
1206 122842 return JS_FALSE;
1207 }
1208
1209 JSBool
1210 js_InCatchBlock(JSTreeContext *tc, JSAtom *atom)
1211 118218 {
1212 JSStmtInfo *stmt;
1213
1214 504203 for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
1215 386087 if (stmt->type == STMT_CATCH && stmt->label == atom)
1216 102 return JS_TRUE;
1217 }
1218 118116 return JS_FALSE;
1219 }
1220
1221 void
1222 js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
1223 ptrdiff_t top)
1224 71506 {
1225 71506 stmt->type = type;
1226 71506 SET_STATEMENT_TOP(stmt, top);
1227 71506 stmt->label = NULL;
1228 71506 stmt->down = tc->topStmt;
1229 71506 tc->topStmt = stmt;
1230 }
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 6526 {
1239 ptrdiff_t offset, delta;
1240
1241 6526 offset = CG_OFFSET(cg);
1242 6526 delta = offset - *lastp;
1243 6526 *lastp = offset;
1244 JS_ASSERT(delta > 0);
1245 6526 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 9076 {
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 9076 if (returnop) {
1273 JS_ASSERT(*returnop == JSOP_RETURN);
1274 11798 for (stmt = cg->treeContext.topStmt; stmt != toStmt;
1275 6562 stmt = stmt->down) {
1276 6562 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 2618 if (*returnop == JSOP_RETURN)
1290 2618 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 6458 depth = cg->stackDepth;
1300 8005 for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
1301 1547 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 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 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 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 6458 cg->stackDepth = depth;
1344 6458 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 6458 {
1351 intN index;
1352
1353 6458 if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
1354 0 return -1;
1355
1356 6458 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 6458 } else if (noteType != SRC_NULL) {
1365 493 if (js_NewSrcNote(cx, cg, noteType) < 0)
1366 0 return -1;
1367 }
1368
1369 6458 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 72740 {
1376 jsbytecode *pc, *stop;
1377 ptrdiff_t delta, span;
1378
1379 72740 pc = CG_CODE(cg, last);
1380 72740 stop = CG_CODE(cg, -1);
1381 152006 while (pc != stop) {
1382 6526 delta = GetJumpOffset(cg, pc);
1383 6526 span = PTRDIFF(target, pc, jsbytecode);
1384 6526 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 6526 *pc = op;
1392 6526 pc -= delta;
1393 }
1394 72740 return JS_TRUE;
1395 }
1396
1397 void
1398 js_PopStatement(JSTreeContext *tc)
1399 71506 {
1400 71506 tc->topStmt = tc->topStmt->down;
1401 }
1402
1403 JSBool
1404 js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
1405 36353 {
1406 JSStmtInfo *stmt;
1407
1408 36353 stmt = cg->treeContext.topStmt;
1409 36353 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 36353 js_PopStatement(&cg->treeContext);
1415 36353 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 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 636684 {
1646 uint32 mode;
1647 JSOp prefixOp;
1648 ptrdiff_t off;
1649 jsbytecode *pc;
1650
1651 636684 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 636684 EMIT_UINT16_IMM_OP(op, atomIndex);
1693 636684 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 608047 {
1710 JSAtomListElement *ale;
1711
1712 608047 ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
1713 608047 if (!ale)
1714 0 return JS_FALSE;
1715 608047 if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg))
1716 0 return JS_FALSE;
1717 608047 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 525149 {
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 525149 if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
1752 14212 return JS_TRUE;
1753
1754 /* QNAME references can never be optimized to use arg/var storage. */
1755 510937 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 510937 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 510937 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 510937 fp = cx->fp;
1782 510937 obj = fp->varobj;
1783 510937 clasp = OBJ_GET_CLASS(cx, obj);
1784 510937 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 396221 optimizeGlobals = (tc->globalUses >= 100 ||
1791 (tc->loopyGlobalUses &&
1792 tc->loopyGlobalUses >= tc->globalUses / 2));
1793 396221 if (!optimizeGlobals)
1794 393960 return JS_TRUE;
1795 } else {
1796 114716 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 116977 atom = pn->pn_atom;
1805 116977 if (fp->scopeChain != obj ||
1806 js_InWithStatement(tc) ||
1807 js_InCatchBlock(tc, atom)) {
1808 102 return JS_TRUE;
1809 }
1810
1811 116875 op = pn->pn_op;
1812 116875 getter = NULL;
1813 #ifdef __GNUC__
1814 116875 attrs = slot = 0; /* quell GCC overwarning */
1815 #endif
1816 116875 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 2261 ATOM_LIST_SEARCH(ale, &tc->decls, atom);
1823 2261 if (!ale) {
1824 /* Use precedes declaration, or name is never declared. */
1825 918 return JS_TRUE;
1826 }
1827
1828 1343 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 1343 ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList);
1835 1343 if (!ale)
1836 0 return JS_FALSE;
1837
1838 /* Defend against tc->numGlobalVars 16-bit overflow. */
1839 1343 slot = ALE_INDEX(ale);
1840 1343 if ((slot + 1) >> 16)
1841 0 return JS_TRUE;
1842
1843 1343 if ((uint16)(slot + 1) > tc->numGlobalVars)
1844 102 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 114614 if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
1854 0 return JS_FALSE;
1855 114614 sprop = (JSScopeProperty *) prop;
1856 114614 if (sprop) {
1857 60928 if (pobj == obj) {
1858 60928 getter = sprop->getter;
1859 60928 attrs = sprop->attrs;
1860 60928 slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1;
1861 }
1862 60928 OBJ_DROP_PROPERTY(cx, pobj, prop);
1863 }
1864 }
1865
1866 115957 if (optimizeGlobals || getter) {
1867 62271 if (optimizeGlobals) {
1868 1343 switch (op) {
1869 1139 case JSOP_NAME: op = JSOP_GETGVAR; break;
1870 204 case JSOP_SETNAME: op = JSOP_SETGVAR; break;
1871 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 60928 } else if (getter == js_GetLocalVariable ||
1881 getter == js_GetCallVariable) {
1882 27370 switch (op) {
1883 19482 case JSOP_NAME: op = JSOP_GETVAR; break;
1884 7752 case JSOP_SETNAME: op = JSOP_SETVAR; break;
1885 0 case JSOP_SETCONST: op = JSOP_SETVAR; break;
1886 102 case JSOP_INCNAME: op = JSOP_INCVAR; break;
1887 34 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 33558 } else if (getter == js_GetArgument ||
1895 (getter == js_CallClass.getProperty &&
1896 fp->fun && (uintN) slot < fp->fun->nargs)) {
1897 33558 switch (op) {
1898 33456 case JSOP_NAME: op = JSOP_GETARG; break;
1899 102 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 62271 if (op != pn->pn_op) {
1910 62271 pn->pn_op = op;
1911 62271 pn->pn_slot = slot;
1912 }
1913 62271 pn->pn_attrs = attrs;
1914 }
1915
1916 115957 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 53686 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 53686 tc->flags |= TCF_FUN_USES_NONLOCALS;
1931 }
1932 115957 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 43350 {
1951 JSBool ok;
1952 JSFunction *fun;
1953 JSParseNode *pn2;
1954
1955 43350 ok = JS_TRUE;
1956 43350 if (!pn || *answer)
1957 0 return ok;
1958
1959 43350 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 break;
1972
1973 case PN_LIST:
1974 72488 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 36244 *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 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 6732 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 6732 *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 break;
2028
2029 case PN_UNARY:
2030 748 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 374 *answer = JS_TRUE;
2036 } else {
2037 0 ok = CheckSideEffects(cx, tc, pn->pn_kid, answer);
2038 }
2039 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 43350 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 39161 {
2089 39161 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 30474 {
2099 JSParseNode *pn2, *pndot, *pnup, *pndown;
2100 ptrdiff_t top;
2101
2102 30474 pn2 = pn->pn_expr;
2103 30474 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 17537 if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
2108 0 return JS_FALSE;
2109 17537 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 30474 if (pn2->pn_type == TOK_DOT) {
2121 5585 pndot = pn2;
2122 5585 pnup = NULL;
2123 5585 top = CG_OFFSET(cg);
2124 for (;;) {
2125 /* Reverse pndot->pn_expr to point up, not down. */
2126 5667 pndot->pn_offset = top;
2127 5667 pndown = pndot->pn_expr;
2128 5667 pndot->pn_expr = pnup;
2129 5667 if (pndown->pn_type != TOK_DOT)
2130 82 break;
2131 82 pnup = pndot;
2132 82 pndot = pndown;
2133 82 }
2134
2135 /* pndown is a primary expression, not a dotted property reference. */
2136 5585 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 5667 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 5667 if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg))
2146 0 return JS_FALSE;
2147
2148 /* Reverse the pn_expr link again. */
2149 5667 pnup = pndot->pn_expr;
2150 5667 pndot->pn_expr = pndown;
2151 5667 pndown = pndot;
2152 5667 } while ((pndot = pnup) != NULL);
2153 } else {
2154 24889 if (!js_EmitTree(cx, cg, pn2))
2155 0 return JS_FALSE;
2156 }
2157
2158 30474 if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op),
2159 CG_OFFSET(cg) - pn2->pn_offset) < 0) {
2160 0 return JS_FALSE;
2161 }
2162 30474 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 30474 if (!EmitAtomOp(cx, pn, op, cg))
2168 0 return JS_FALSE;
2169 }
2170 30474 return JS_TRUE;
2171 }
2172
2173 static JSBool
2174 EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
2175 19100 {
2176 ptrdiff_t top;
2177 JSParseNode *left, *right, *next, temp;
2178 jsint slot;
2179
2180 19100 top = CG_OFFSET(cg);
2181 19100 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 19100 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 19100 left = pn->pn_left;
2243 19100 right = pn->pn_right;
2244 }
2245
2246 /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */
2247 19100 if (op == JSOP_GETELEM &&
2248 left->pn_type == TOK_NAME &&
2249 right->pn_type == TOK_NUMBER) {
2250 8498 if (!LookupArgOrVar(cx, &cg->treeContext, left))
2251 0 return JS_FALSE;
2252 8498 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 19100 if (!js_EmitTree(cx, cg, left))
2262 0 return JS_FALSE;
2263 }
2264 19100 if (!js_EmitTree(cx, cg, right))
2265 0 return JS_FALSE;
2266 19100 if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
2267 0 return JS_FALSE;
2268 19100 return js_Emit1(cx, cg, op) >= 0;
2269 }
2270
2271 static JSBool
2272 EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
2273 40252 {
2274 jsint ival;
2275 jsatomid atomIndex;
2276 ptrdiff_t off;
2277 jsbytecode *pc;
2278 JSAtom *atom;
2279 JSAtomListElement *ale;
2280
2281 40762 if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
2282 40252 if (ival == 0)
2283 12573 return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
2284 27679 if (ival == 1)
2285 10073 return js_Emit1(cx, cg, JSOP_ONE) >= 0;
2286
2287 17606 atomIndex = (jsatomid)ival;
2288 17606 if (atomIndex < JS_BIT(16)) {
2289 17096 EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex);
2290 17096 return JS_TRUE;
2291 }
2292
2293 510 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 510 atom = js_AtomizeInt(cx, ival, 0);
2303 } else {
2304 0 atom = js_AtomizeDouble(cx, dval, 0);
2305 }
2306 510 if (!atom)
2307 0 return JS_FALSE;
2308
2309 510 ale = js_IndexAtom(cx, atom, &cg->atomList);
2310 510 if (!ale)
2311 0 return JS_FALSE;
2312 510 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 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 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 3774 {
2766 JSStackFrame *fp, frame;
2767 JSObject *funobj;
2768 JSBool ok;
2769
2770 3774 if (!js_AllocTryNotes(cx, cg))
2771 0 return JS_FALSE;
2772
2773 3774 fp = cx->fp;
2774 3774 funobj = fun->object;
2775 JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
2776 fp->scopeChain != funobj));
2777 3774 memset(&frame, 0, sizeof frame);
2778 3774 frame.fun = fun;
2779 3774 frame.varobj = frame.scopeChain = funobj;
2780 3774 frame.down = fp;
2781 3774 frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
2782 ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
2783 : JSFRAME_COMPILING;
2784 3774 cx->fp = &frame;
2785 3774 ok = js_EmitTree(cx, cg, body);
2786 3774 cx->fp = fp;
2787 3774 if (!ok)
2788 0 return JS_FALSE;
2789
2790 3774 fun->u.script = js_NewScriptFromCG(cx, cg, fun);
2791 3774 if (!fun->u.script)
2792 0 return JS_FALSE;
2793 JS_ASSERT(fun->interpreted);
2794 3774 if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
2795 0 fun->flags |= JSFUN_HEAVYWEIGHT;
2796 3774 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 4651 {
2833 4651 UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
2834 4651 return JS_TRUE;
2835 }
2836
2837 JSBool
2838 js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
2839 1321714 {
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 1321714 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 1321714 ok = JS_TRUE;
2860 1321714 cg->emitLevel++;
2861 1321714 pn->pn_offset = top = CG_OFFSET(cg);
2862
2863 /* Emit notes to tell the current bytecode's source line number. */
2864 1321714 UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
2865
2866 1321714 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 3774 if (pn->pn_arity == PN_NULLARY) {
2875 0 if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0)
2876 0 return JS_FALSE;
2877 break;
2878 }
2879 #endif
2880
2881 /* Generate code for the function's body. */
2882 3774 cg2mark = JS_ARENA_MARK(&cx->tempPool);
2883 3774 JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool);
2884 3774 if (!cg2) {
2885 0 JS_ReportOutOfMemory(cx);
2886 0 return JS_FALSE;
2887 }
2888 3774 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 3774 cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
2894 3774 cg2->treeContext.tryCount = pn->pn_tryCount;
2895 3774 cg2->parent = cg;
2896 3774 fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
2897 3774 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 3774 if (cg2->treeContext.flags &
2905 (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {
2906 3468 cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
2907 }
2908 3774 js_FinishCodeGenerator(cx, cg2);
2909 3774 JS_ARENA_RELEASE(&cx->tempPool, cg2mark);
2910
2911 /* Make the function object a literal in the outer script's pool. */
2912 3774 ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList);
2913 3774 if (!ale)
2914 0 return JS_FALSE;
2915 3774 atomIndex = ALE_INDEX(ale);
2916
2917 #if JS_HAS_LEXICAL_CLOSURE
2918 /* Emit a bytecode pointing to the closure object in its immediate. */
2919 3774 if (pn->pn_op != JSOP_NOP) {
2920 0 EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
2921 break;
2922 }
2923 #endif
2924
2925 /* Top-level named functions need a nop for decompilation. */
2926 3774 noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);
2927 3774 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 3774 CG_SWITCH_TO_PROLOG(cg);
2937
2938 #if JS_HAS_LEXICAL_CLOSURE
2939 3774 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 3774 EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
2982
2983 3774 CG_SWITCH_TO_MAIN(cg);
2984 3774 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 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 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 11458 stmtInfo.type = STMT_IF;
3027 11458 beq = jmp = -1;
3028 11458 noteIndex = -1;
3029
3030 14100 if_again:
3031 /* Emit code for the condition before pushing stmtInfo. */
3032 14100 if (!js_EmitTree(cx, cg, pn->pn_kid1))
3033 0 return JS_FALSE;
3034 14100 if (stmtInfo.type == STMT_IF) {
3035 11458 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 2642 stmtInfo.type = STMT_IF;
3046 2642 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 14100 pn3 = pn->pn_kid3;
3052 14100 noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
3053 14100 if (noteIndex < 0)
3054 0 return JS_FALSE;
3055 14100 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
3056 14100 if (beq < 0)
3057 0 return JS_FALSE;
3058
3059 /* Emit code for the then and optional else parts. */
3060 14100 if (!js_EmitTree(cx, cg, pn->pn_kid2))
3061 0 return JS_FALSE;
3062 14100 if (pn3) {
3063 /* Modify stmtInfo so we know we're in the else part. */
3064 5931 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 5931 jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL);
3073 5931 if (jmp < 0)
3074 0 return JS_FALSE;
3075
3076 /* Ensure the branch-if-false comes here, then emit the else. */
3077 5931 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
3078 5931 if (pn3->pn_type == TOK_IF) {
3079 2642 pn = pn3;
3080 2642 goto if_again;
3081 }
3082
3083 3289 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 3289 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 8169 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
3098 }
3099 11458 ok = js_PopStatementCG(cx, cg);
3100 11458 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 238 js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
3111 238 if (!js_EmitTree(cx, cg, pn->pn_left))
3112 0 return JS_FALSE;
3113 238 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
3114 238 if (noteIndex < 0)
3115 0 return JS_FALSE;
3116 238 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
3117 238 if (beq < 0)
3118 0 return JS_FALSE;
3119 238 if (!js_EmitTree(cx, cg, pn->pn_right))
3120 0 return JS_FALSE;
3121 238 jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
3122 238 if (jmp < 0)
3123 0 return JS_FALSE;
3124 238 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
3125 238 if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
3126 0 return JS_FALSE;
3127 238 ok = js_PopStatementCG(cx, cg);
3128 238 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 1560 beq = 0; /* suppress gcc warnings */
3168 1560 pn2 = pn->pn_left;
3169 1560 js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);
3170
3171 1560 if (pn2->pn_type == TOK_IN) {
3172 JSBool emitIFEQ;
3173
3174 /* Set stmtInfo type for later testing. */
3175 1492 stmtInfo.type = STMT_FOR_IN_LOOP;
3176 1492 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 1492 pn3 = pn2->pn_left;
3199 1492 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 1492 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 1492 if (!js_EmitTree(cx, cg, pn2->pn_right))
3208 0 return JS_FALSE;
3209 1492 if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0)
3210 0 return JS_FALSE;
3211
3212 1492 top = CG_OFFSET(cg);
3213 1492 SET_STATEMENT_TOP(&stmtInfo, top);
3214
3215 #if JS_HAS_XML_SUPPORT
3216 /* Emit a prefix opcode if 'for each (... in ...)' was used. */
3217 1492 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 1492 emitIFEQ = JS_TRUE;
3223 1492 switch (pn3->pn_type) {
3224 case TOK_VAR:
3225 1492 pn3 = pn3->pn_head;
3226 JS_ASSERT(pn3->pn_type == TOK_NAME);
3227 1492 if (!pn3->pn_expr && js_NewSrcNote(cx, cg, SRC_VAR) < 0)
3228 0 return JS_FALSE;
3229 /* FALL THROUGH */
3230 case TOK_NAME:
3231 1492 if (pn3->pn_slot >= 0) {
3232 799 op = pn3->pn_op;
3233 799 switch (op) {
3234 case JSOP_GETARG: /* FALL THROUGH */
3235 0 case JSOP_SETARG: op = JSOP_FORARG; break;
3236 case JSOP_GETVAR: /* FALL THROUGH */
3237 782 case JSOP_SETVAR: op = JSOP_FORVAR; break;
3238 case JSOP_GETGVAR:
3239 17 case JSOP_SETGVAR: op = JSOP_FORNAME; break;
3240 default: JS_ASSERT(0);
3241 }
3242 } else {
3243 693 pn3->pn_op = JSOP_FORNAME;
3244 693 if (!LookupArgOrVar(cx, &cg->treeContext, pn3))
3245 0 return JS_FALSE;
3246 693 op = pn3->pn_op;
3247 }
3248 1492 if (pn3->pn_slot >= 0) {
3249 799 if (pn3->pn_attrs & JSPROP_READONLY)
3250 0 op = JSOP_GETVAR;
3251 799 atomIndex = (jsatomid) pn3->pn_slot;
3252 799 EMIT_UINT16_IMM_OP(op, atomIndex);
3253 } else {
3254 693 if (!EmitAtomOp(cx, pn3, op, cg))
3255 0 return JS_FALSE;
3256 }
3257 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 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 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 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 1492 if (emitIFEQ) {
3334 /* Annotate so the decompiler can find the loop-closing jump. */
3335 1492 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
3336 1492 if (noteIndex < 0)
3337 0 return JS_FALSE;
3338
3339 /* Pop and test the loop condition generated by JSOP_FOR*. */
3340 1492 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
3341 1492 if (beq < 0)
3342 0 return JS_FALSE;
3343 }
3344 } else {
3345 68 if (!pn2->pn_kid1) {
3346 /* No initializer: emit an annotated nop for the decompiler. */
3347 0 op = JSOP_NOP;
3348 } else {
3349 68 if (!js_EmitTree(cx, cg, pn2->pn_kid1))
3350 0 return JS_FALSE;
3351 68 op = JSOP_POP;
3352 }
3353 68 noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
3354 68 if (noteIndex < 0 ||
3355 js_Emit1(cx, cg, op) < 0) {
3356 0 return JS_FALSE;
3357 }
3358
3359 68 top = CG_OFFSET(cg);
3360 68 SET_STATEMENT_TOP(&stmtInfo, top);
3361 68 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 68 if (!js_EmitTree(cx, cg, pn2->pn_kid2))
3367 0 return JS_FALSE;
3368 68 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
3369 CG_OFFSET(cg) - top)) {
3370 0 return JS_FALSE;
3371 }
3372 68 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
3373 68 if (beq < 0)
3374 0 return JS_FALSE;
3375 }
3376
3377 /* Set pn3 (used below) here to avoid spurious gcc warnings. */
3378 68 pn3 = pn2->pn_kid3;
3379 }
3380
3381 /* Emit code for the loop body. */
3382 1560 if (!js_EmitTree(cx, cg, pn->pn_right))
3383 0 return JS_FALSE;
3384
3385 1560 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 68 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
3389 CG_OFFSET(cg) - top)) {
3390 0 return JS_FALSE;
3391 }
3392
3393 68 if (pn3) {
3394 /* Set loop and enclosing "update" offsets, for continue. */
3395 68 stmt = &stmtInfo;
3396 do {
3397 68 stmt->update = CG_OFFSET(cg);
3398 } while ((stmt = stmt->down) != NULL &&
3399 68 stmt->type == STMT_LABEL);
3400
3401 68 if (!js_EmitTree(cx, cg, pn3))
3402 0 return JS_FALSE;
3403 68 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 68 off = (ptrdiff_t) pn->pn_pos.end.lineno;
3408 68 if (CG_CURRENT_LINE(cg) != (uintN) off) {
3409 68 if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
3410 0 return JS_FALSE;
3411 68 CG_CURRENT_LINE(cg) = (uintN) off;
3412 }
3413 }
3414
3415 /* The third note offset helps us find the loop-closing jump. */
3416 68 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 1560 jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
3424 1560 if (jmp < 0)
3425 0 return JS_FALSE;
3426 1560 if (beq > 0)
3427 1560 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
3428 1560 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 1492 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 1560 if (!js_PopStatementCG(cx, cg))
3437 0 return JS_FALSE;
3438
3439 1560 if (pn2->pn_type == TOK_IN) {
3440 1492 if (js_Emit1(cx, cg, JSOP_POP2) < 0)
3441 0 return JS_FALSE;
3442 }
3443 break;
3444
3445 case TOK_BREAK:
3446 34 stmt = cg->treeContext.topStmt;
3447 34 atom = pn->pn_atom;
3448 34 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 34 ale = NULL;
3457 102 while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
3458 34 stmt = stmt->down;
3459 34 noteType = SRC_NULL;
3460 }
3461
3462 34 if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0)
3463 0 return JS_FALSE;
3464 break;
3465
3466 case TOK_CONTINUE:
3467 493 stmt = cg->treeContext.topStmt;
3468 493 atom = pn->pn_atom;
3469 493 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 493 ale = NULL;
3484 2499 while (!STMT_IS_LOOP(stmt))
3485 1513 stmt = stmt->down;
3486 493 noteType = SRC_CONTINUE;
3487 }
3488
3489 493 if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0)
3490 0 return JS_FALSE;
3491 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 34 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 34 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 34 depth = cg->stackDepth;
3542
3543 /* Mark try location for decompilation, then emit try block. */
3544 34 if (js_Emit1(cx, cg, JSOP_TRY) < 0)
3545 0 return JS_FALSE;
3546 34 start = CG_OFFSET(cg);
3547 34 if (!js_EmitTree(cx, cg, pn->pn_kid1))
3548 0 return JS_FALSE;
3549
3550 /* GOSUB to finally, if present. */
3551 34 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 34 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
3561 0 return JS_FALSE;
3562 34 jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &stmtInfo.catchJump);
3563 34 if (jmp < 0)
3564 0 return JS_FALSE;
3565
3566 34 end = CG_OFFSET(cg);
3567
3568 /* If this try has a catch block, emit it. */
3569 34 iter = pn->pn_kid2;
3570 34 if (iter) {
3571 34 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 34 if (!UpdateLineNumberNotes(cx, cg, iter))
3601 0 return JS_FALSE;
3602
3603 34 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 34 EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
3624 34 cg->stackDepth = depth;
3625 }
3626
3627 /* Non-negative guardnote offset is length of catchguard. */
3628 34 guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
3629 34 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 34 ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,
3636 &cg->atomList);
3637 34 if (!ale)
3638 0 return JS_FALSE;
3639 34 EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
3640
3641 34 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 34 disc = iter->pn_kid1;
3649 34 ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList);
3650 34 if (!ale)
3651 0 return JS_FALSE;
3652
3653 34 EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale));
3654 34 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 34 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 34 js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_CATCH,
3676 CG_OFFSET(cg));
3677 34 stmtInfo2.label = disc->pn_atom;
3678 34 if (!js_EmitTree(cx, cg, iter->pn_kid3))
3679 0 return JS_FALSE;
3680 34 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 34 off = cg->stackDepth - 1;
3689 34 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 34 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 34 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
3705 0 return JS_FALSE;
3706 34 jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
3707 &stmtInfo.catchJump);
3708 34 if (jmp < 0)
3709 0 return JS_FALSE;
3710 34 if (!iter->pn_kid2) /* leave iter at last catch */
3711 0 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 34 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 34 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 34 if (!js_PopStatementCG(cx, cg))
3808 0 return JS_FALSE;
3809
3810 34 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 34 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 34 if (pn->pn_kid2) {
3824 JS_ASSERT(end != -1 && catchStart != -1);
3825 34 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 34 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 break;
3840 }
3841
3842 #endif /* JS_HAS_EXCEPTIONS */
3843
3844 case TOK_VAR:
3845 10442 off = noteIndex = -1;
3846 10499 for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
3847 JS_ASSERT(pn2->pn_type == TOK_NAME);
3848 10499 if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
3849 0 return JS_FALSE;
3850 10499 op = pn2->pn_op;
3851 10499 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 10499 if (pn2->pn_slot >= 0) {
3858 6001 atomIndex = (jsatomid) pn2->pn_slot;
3859 } else {
3860 4498 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
3861 4498 if (!ale)
3862 0 return JS_FALSE;
3863 4498 atomIndex = ALE_INDEX(ale);
3864 }
3865
3866 10499 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 4617 CG_SWITCH_TO_PROLOG(cg);
3871 4617 if (!UpdateLineNumberNotes(cx, cg, pn2))
3872 0 return JS_FALSE;
3873 4617 EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
3874 4617 CG_SWITCH_TO_MAIN(cg);
3875 }
3876
3877 10499 if (pn2->pn_expr) {
3878 8517 if (op == JSOP_SETNAME)
3879 3655 EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
3880 8517 pn3 = pn2->pn_expr;
3881 8517 if (pn->pn_op == JSOP_DEFCONST &&
3882 !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
3883 pn3)) {
3884 0 return JS_FALSE;
3885 }
3886 8517 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 10499 if ((pn->pn_extra & PNX_FORINVAR) && !pn2->pn_expr)
3905 9007 break;
3906
3907 9007 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 9007 if (op == JSOP_ARGUMENTS) {
3915 0 if (js_Emit1(cx, cg, op) < 0)
3916 0 return JS_FALSE;
3917 9007 } else if (pn2->pn_slot >= 0) {
3918 5202 EMIT_UINT16_IMM_OP(op, atomIndex);
3919 } else {
3920 3805 EMIT_ATOM_INDEX_OP(op, atomIndex);
3921 }
3922 9007 tmp = CG_OFFSET(cg);
3923 9007 if (noteIndex >= 0) {
3924 57 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
3925 0 return JS_FALSE;
3926 }
3927 9007 if (!pn2->pn_next)
3928 57 break;
3929 57 off = tmp;
3930 57 noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
3931 57 if (noteIndex < 0 ||
3932 js_Emit1(cx, cg, JSOP_POP) < 0) {
3933 0 return JS_FALSE;
3934 }
3935 57 }
3936 10442 if (pn->pn_extra & PNX_POPVAR) {
3937 8882 if (js_Emit1(cx, cg, JSOP_POP) < 0)
3938 0 return JS_FALSE;
3939 }
3940 break;
3941
3942 case TOK_RETURN:
3943 /* Push a return value */
3944 2618 pn2 = pn->pn_kid;
3945 2618 if (pn2) {
3946 1632 if (!js_EmitTree(cx, cg, pn2))
3947 0 return JS_FALSE;
3948 } else {
3949 986 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 2618 op = JSOP_RETURN;
3962 2618 if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
3963 0 return JS_FALSE;
3964 2618 if (js_Emit1(cx, cg, op) < 0)
3965 0 return JS_FALSE;
3966 break;
3967
3968 case TOK_LC:
3969 #if JS_HAS_XML_SUPPORT
3970 23029 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 break;
3976 }
3977 #endif
3978
3979 JS_ASSERT(pn->pn_arity == PN_LIST);
3980 23029 js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
3981 124203 for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3982 101174 if (!js_EmitTree(cx, cg, pn2))
3983 0 return JS_FALSE;
3984 }
3985 23029 ok = js_PopStatementCG(cx, cg);
3986 23029 break;
3987
3988 case TOK_SEMI:
3989 264525 pn2 = pn->pn_kid;
3990 264525 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 264525 useful = wantval = !cx->fp->fun ||
3998 !cx->fp->fun->interpreted ||
3999 (cx->fp->flags & JSFRAME_SPECIAL);
4000 264525 if (!useful) {
4001 43350 if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
4002 0 return JS_FALSE;
4003 }
4004 264525 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 264525 if (!js_EmitTree(cx, cg, pn2))
4015 0 return JS_FALSE;
4016 264525 if (js_Emit1(cx, cg, wantval ? JSOP_POPV : JSOP_POP) < 0)
4017 0 return JS_FALSE;
4018 }
4019 }
4020 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 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 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 9540 pn2 = pn->pn_left;
4090 JS_ASSERT(pn2->pn_type != TOK_RP);
4091 9540 atomIndex = (jsatomid) -1; /* Suppress warning. */
4092 9540 switch (pn2->pn_type) {
4093 case TOK_NAME:
4094 6520 if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
4095 0 return JS_FALSE;
4096 6520 if (pn2->pn_slot >= 0) {
4097 3196 atomIndex = (jsatomid) pn2->pn_slot;
4098 } else {
4099 3324 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
4100 3324 if (!ale)
4101 0 return JS_FALSE;
4102 3324 atomIndex = ALE_INDEX(ale);
4103 3324 EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
4104 }
4105 break;
4106 case TOK_DOT:
4107 340 if (!js_EmitTree(cx, cg, pn2->pn_expr))
4108 0 return JS_FALSE;
4109 340 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
4110 340 if (!ale)
4111 0 return JS_FALSE;
4112 340 atomIndex = ALE_INDEX(ale);
4113 340 break;
4114 case TOK_LB:
4115 JS_ASSERT(pn->pn_arity == PN_BINARY);
4116 2680 if (!js_EmitTree(cx, cg, pn2->pn_left))
4117 0 return JS_FALSE;
4118 2680 if (!js_EmitTree(cx, cg, pn2->pn_right))
4119 0 return JS_FALSE;
4120 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 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 9540 op = pn->pn_op;
4141 #if JS_HAS_GETTER_SETTER
4142 9540 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 9540 if (op != JSOP_NOP) {
4148 17 switch (pn2->pn_type) {
4149 case TOK_NAME:
4150 17 if (pn2->pn_op != JSOP_SETNAME) {
4151 0 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 break;
4158 }
4159 /* FALL THROUGH */
4160 case TOK_DOT:
4161 17 if (js_Emit1(cx, cg, JSOP_DUP) < 0)
4162 0 return JS_FALSE;
4163 17 EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex);
4164 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 9540 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 9540 if (op != JSOP_NOP) {
4187 17 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 9540 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 9540 switch (pn2->pn_type) {
4202 case TOK_NAME:
4203 6520 if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) {
4204 6520 if (pn2->pn_slot >= 0) {
4205 3196 EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex);
4206 } else {
4207 case TOK_DOT:
4208 3664 EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
4209 }
4210 }
4211 break;
4212 case TOK_LB:
4213 #if JS_HAS_LVALUE_RETURN
4214 case TOK_LP:
4215 #endif
4216 2680 if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
4217 0 return JS_FALSE;
4218 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 break;
4228
4229 case TOK_HOOK:
4230 /* Emit the condition, then branch if false to the else part. */
4231 0 if (!js_EmitTree(cx, cg, pn->pn_kid1))
4232 0 return JS_FALSE;
4233 0 noteIndex = js_NewSrcNote(cx, cg, SRC_COND);
4234 0 if (noteIndex < 0)
4235 0 return JS_FALSE;
4236 0 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
4237 0 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 0 jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
4242 0 if (jmp < 0)
4243 0 return JS_FALSE;
4244 0 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
4245 0 if (!js_EmitTree(cx, cg, pn->pn_kid3))
4246 0 return JS_FALSE;
4247 0 CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
4248 0 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 0 cg->stackDepth--;
4261 0 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 3451 pn3 = pn;
4281 3451 if (!js_EmitTree(cx, cg, pn->pn_left))
4282 0 return JS_FALSE;
4283 3451 top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
4284 3451 if (top < 0)
4285 0 return JS_FALSE;
4286 3451 jmp = top;
4287 3451 pn2 = pn->pn_right;
4288 7688 while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) {
4289 786 pn = pn2;
4290 786 if (!js_EmitTree(cx, cg, pn->pn_left))
4291 0 return JS_FALSE;
4292 786 off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
4293 786 if (off < 0)
4294 0 return JS_FALSE;
4295 786 if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp))
4296 0 return JS_FALSE;
4297 786 jmp = off;
4298 786 pn2 = pn->pn_right;
4299 }
4300 3451 if (!js_EmitTree(cx, cg, pn2))
4301 0 return JS_FALSE;
4302 3451 off = CG_OFFSET(cg);
4303 do {
4304 4237 pc = CG_CODE(cg, top);
4305 4237 tmp = GetJumpOffset(cg, pc);
4306 4237 CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
4307 4237 *pc = pn3->pn_op;
4308 4237 top += tmp;
4309 4237 } while ((pn3 = pn3->pn_right) != pn2);
4310 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 22679 if (pn->pn_arity == PN_LIST) {
4329 /* Left-associative operator chain: avoid too much recursion. */
4330 1097 pn2 = pn->pn_head;
4331 1097 if (!js_EmitTree(cx, cg, pn2))
4332 0 return JS_FALSE;
4333 1097 op = pn->pn_op;
4334 4622 while ((pn2 = pn2->pn_next) != NULL) {
4335 2428 if (!js_EmitTree(cx, cg, pn2))
4336 0 return JS_FALSE;
4337 2428 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 21582 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 break;
4349 }
4350 #endif
4351 /* Binary operators that evaluate both operands unconditionally. */
4352 21582 if (!js_EmitTree(cx, cg, pn->pn_left))
4353 0 return JS_FALSE;
4354 21582 if (!js_EmitTree(cx, cg, pn->pn_right))
4355 0 return JS_FALSE;
4356 21582 if (js_Emit1(cx, cg, pn->pn_op) < 0)
4357 0 return JS_FALSE;
4358 }
4359 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 555 pn2 = pn->pn_kid;
4373 555 if (!js_EmitTree(cx, cg, pn2))
4374 0 return JS_FALSE;
4375 555 op = pn->pn_op;
4376 #if JS_HAS_XML_SUPPORT
4377 555 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 555 if (js_Emit1(cx, cg, op) < 0)
4384 0 return JS_FALSE;
4385 break;
4386
4387 case TOK_INC:
4388 case TOK_DEC:
4389 /* Emit lvalue-specialized code for ++/-- operators. */
4390 442 pn2 = pn->pn_kid;
4391 JS_ASSERT(pn2->pn_type != TOK_RP);
4392 442 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 442 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 442 switch (pn2->pn_type) {
4408 case TOK_NAME:
4409 136 pn2->pn_op = op;
4410 136 if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
4411 0 return JS_FALSE;
4412 136 op = pn2->pn_op;
4413 136 if (pn2->pn_slot >= 0) {
4414 136 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 136 atomIndex = (jsatomid) pn2->pn_slot;
4421 136 EMIT_UINT16_IMM_OP(op, atomIndex);
4422 } else {
4423 0 if (!EmitAtomOp(cx, pn2, op, cg))
4424 0 return JS_FALSE;
4425 }
4426 break;
4427 case TOK_DOT:
4428 0 if (!EmitPropOp(cx, pn2, op, cg))
4429 0 return JS_FALSE;
4430 break;
4431 case TOK_LB:
4432 306 if (!EmitElemOp(cx, pn2, op, cg))
4433 0 return JS_FALSE;
4434 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 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 442 if (pn2->pn_type != TOK_NAME && (js_CodeSpec[op].format & JOF_POST))
4463 306 --cg->stackDepth;
4464 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 break;
4486 case TOK_DOT:
4487 0 if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))
4488 0 return JS_FALSE;
4489 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 break;
4495 #endif
4496 case TOK_LB:
4497 0 if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))
4498 0 return JS_FALSE;
4499 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 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 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 30474 ok = EmitPropOp(cx, pn, pn->pn_op, cg);
4539 30474 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 18794 ok = EmitElemOp(cx, pn, pn->pn_op, cg);
4552 18794 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 268145 pn2 = pn->pn_head;
4567 #if JS_HAS_XML_SUPPORT
4568 268145 if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) {
4569 JS_ASSERT(pn2->pn_op == JSOP_GETPROP);
4570 3678 pn2->pn_op = JSOP_GETMETHOD;
4571 3678 pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE;
4572 }
4573 #endif
4574 268145 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 268145 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 268145 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 550321 for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
4593 282176 if (!js_EmitTree(cx, cg, pn2))
4594 0 return JS_FALSE;
4595 }
4596 268145 if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)
4597 0 return JS_FALSE;
4598
4599 268145 argc = pn->pn_count - 1;
4600 268145 if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
4601 0 return JS_FALSE;
4602 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 5203 ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom,
4613 &cg->atomList);
4614 5203 if (!ale)
4615 0 return JS_FALSE;
4616 5203 EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
4617 5203 if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
4618 0 return JS_FALSE;
4619 5203 if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
4620 0 return JS_FALSE;
4621
4622 5203 pn2 = pn->pn_head;
4623 #if JS_HAS_SHARP_VARS
4624 5203 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 28245 for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
4631 23042 if (!EmitNumberOp(cx, atomIndex, cg))
4632 0 return JS_FALSE;
4633
4634 /* FIXME 260106: holes in a sparse initializer are void-filled. */
4635 23042 if (pn2->pn_type == TOK_COMMA) {
4636 0 if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
4637 0 return JS_FALSE;
4638 } else {
4639 23042 if (!js_EmitTree(cx, cg, pn2))
4640 0 return JS_FALSE;
4641 }
4642
4643 23042 if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
4644 0 return JS_FALSE;
4645 }
4646
4647 5203 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 5203 if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
4655 0 return JS_FALSE;
4656 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 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 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 break;
4735
4736 case TOK_USESHARP:
4737 0 EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
4738 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 585 if (!js_EmitTree(cx, cg, pn->pn_kid))
4749 0 return JS_FALSE;
4750 585 if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
4751 0 return JS_FALSE;
4752 break;
4753
4754 case TOK_NAME:
4755 481266 if (!LookupArgOrVar(cx, &cg->treeContext, pn))
4756 0 return JS_FALSE;
4757 481266 op = pn->pn_op;
4758 481266 if (op == JSOP_ARGUMENTS) {
4759 0 if (js_Emit1(cx, cg, op) < 0)
4760 0 return JS_FALSE;
4761 break;
4762 }
4763 481266 if (pn->pn_slot >= 0) {
4764 52938 atomIndex = (jsatomid) pn->pn_slot;
4765 52938 EMIT_UINT16_IMM_OP(op, atomIndex);
4766 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 571213 ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
4791 571213 break;
4792
4793 case TOK_NUMBER:
4794 17210 ok = EmitNumberOp(cx, pn->pn_dval, cg);
4795 17210 break;
4796
4797 #if JS_HAS_XML_SUPPORT
4798 case TOK_ANYNAME:
4799 #endif
4800 case TOK_PRIMARY:
4801 2280 if (js_Emit1(cx, cg, pn->pn_op) < 0)
4802 0 return JS_FALSE;
4803 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 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 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 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 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 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 1321714 if (ok && --cg->emitLevel == 0 && cg->spanDeps)
4959 0 ok = OptimizeSpanDeps(cx, cg);
4960
4961 1321714 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 1350135 {
4995 intN index;
4996 JSArenaPool *pool;
4997 size_t size;
4998
4999 1350135 index = CG_NOTE_COUNT(cg);
5000 1350135 if (((uintN)index & CG_NOTE_MASK(cg)) == 0) {
5001 11918 pool = cg->notePool;
5002 11918 size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
5003 11918 if (!CG_NOTES(cg)) {
5004 /* Allocate the first note array lazily; leave noteMask alone. */
5005 5201 JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size);
5006 } else {
5007 /* Grow by doubling note array size; update noteMask on success. */
5008 6717 JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
5009 6717 if (CG_NOTES(cg))
5010 6717 CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
5011 }
5012 11918 if (!CG_NOTES(cg)) {
5013 0 JS_ReportOutOfMemory(cx);
5014 0 return -1;
5015 }
5016 }
5017
5018 1350135 CG_NOTE_COUNT(cg) = index + 1;
5019 1350135 return index;
5020 }
5021
5022 intN
5023 js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
5024 1319445 {
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 1319445 index = AllocSrcNote(cx, cg);
5034 1319445 if (index < 0)
5035 0 return -1;
5036 1319445 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 1319445 offset = CG_OFFSET(cg);
5043 1319445 delta = offset - CG_LAST_NOTE_OFFSET(cg);
5044 1319445 CG_LAST_NOTE_OFFSET(cg) = offset;
5045 1319445 if (delta >= SN_DELTA_LIMIT) {
5046 do {
5047 30690 xdelta = JS_MIN(delta, SN_XDELTA_MASK);
5048 30690 SN_MAKE_XDELTA(sn, xdelta);
5049 30690 delta -= xdelta;
5050 30690 index = AllocSrcNote(cx, cg);
5051 30690 if (index < 0)
5052 0 return -1;
5053 30690 sn = &CG_NOTES(cg)[index];
5054 30690 } 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 1319445 SN_MAKE_NOTE(sn, type, delta);
5063 1727212 for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) {
5064 407767 if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
5065 0 return -1;
5066 }
5067 1319445 return index;
5068 }
5069
5070 intN
5071 js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
5072 ptrdiff_t offset)
5073 399902 {
5074 intN index;
5075
5076 399902 index = js_NewSrcNote(cx, cg, type);
5077 399902 if (index >= 0) {
5078 399902 if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
5079 0 return -1;
5080 }
5081 399902 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 201 {
5103 JSArenaPool *pool;
5104 size_t size;
5105
5106 /* Grow by doubling note array size; update noteMask on success. */
5107 201 pool = cg->notePool;
5108 201 size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
5109 201 JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
5110 201 if (!CG_NOTES(cg)) {
5111 0 JS_ReportOutOfMemory(cx);
5112 0 return JS_FALSE;
5113 }
5114 201 CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
5115 201 return JS_TRUE;
5116 }
5117
5118 jssrcnote *
5119 js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
5120 ptrdiff_t delta)
5121 204 {
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 204 base = SN_DELTA(sn);
5133 204 limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
5134 204 newdelta = base + delta;
5135 204 if (newdelta < limit) {
5136 68 SN_SET_DELTA(sn, newdelta);
5137 } else {
5138 136 index = sn - cg->main.notes;
5139 136 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 136 diff = cg->main.noteCount - index;
5145 136 cg->main.noteCount++;
5146 136 memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
5147 136 SN_MAKE_XDELTA(sn, delta);
5148 136 sn++;
5149 }
5150 204 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 407824 {
5189 jssrcnote *sn;
5190 ptrdiff_t diff;
5191
5192 407824 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 407824 sn = &CG_NOTES(cg)[index];
5199 JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
5200 JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
5201 408028 for (sn++; which; sn++, which--) {
5202 204 if (*sn & SN_3BYTE_OFFSET_FLAG)
5203 0 sn += 2;
5204 }
5205
5206 /* See if the new offset requires three bytes. */
5207 407824 if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
5208 /* Maybe this offset was already set to a three-byte value. */
5209 12227 if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
5210 /* Losing, need to insert another two bytes for this offset. */
5211 12227 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 12227 if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {
5219 201 if (!GrowSrcNotes(cx, cg))
5220 0 return JS_FALSE;
5221 201 sn = CG_NOTES(cg) + index;
5222 }
5223 12227 CG_NOTE_COUNT(cg) += 2;
5224
5225 12227 diff = CG_NOTE_COUNT(cg) - (index + 3);
5226 JS_ASSERT(diff >= 0);
5227 12227 if (diff > 0)
5228 1889 memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
5229 }
5230 12227 *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
5231 12227 *sn++ = (jssrcnote)(offset >> 8);
5232 }
5233 407824 *sn = (jssrcnote)offset;
5234 407824 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 5000 {
5272 uintN prologCount, mainCount, totalCount;
5273 ptrdiff_t offset, delta;
5274 jssrcnote *sn;
5275
5276 JS_ASSERT(cg->current == &cg->main);
5277
5278 5000 prologCount = cg->prolog.noteCount;
5279 5371 if (prologCount && cg->prolog.currentLine != cg->firstLine) {
5280 371 CG_SWITCH_TO_PROLOG(cg);
5281 371 if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0)
5282 0 return JS_FALSE;
5283 371 prologCount = cg->prolog.noteCount;
5284 371 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 4629 offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset;
5294 JS_ASSERT(offset >= 0);
5295 4629 if (offset > 0) {
5296 /* NB: Use as much of the first main note's delta as we can. */
5297 68 sn = cg->main.notes;
5298 68 delta = SN_IS_XDELTA(sn)
5299 ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
5300 : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
5301 68 if (offset < delta)
5302 0 delta = offset;
5303 for (;;) {
5304 204 if (!js_AddToSrcNoteDelta(cx, cg, sn, delta))
5305 0 return JS_FALSE;
5306 204 offset -= delta;
5307 204 if (offset == 0)
5308 136 break;
5309 136 delta = JS_MIN(offset, SN_XDELTA_MASK);
5310 136 sn = cg->main.notes;
5311 136 }
5312 }
5313 }
5314
5315 5000 mainCount = cg->main.noteCount;
5316 5000 totalCount = prologCount + mainCount;
5317 5000 if (prologCount)
5318 371 memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount));
5319 5000 memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
5320 5000 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 5000 return JS_TRUE;
5330 }
5331
5332 JSBool
5333 js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
5334 196291 {
5335 size_t size, incr;
5336 ptrdiff_t delta;
5337
5338 196291 size = TRYNOTE_SIZE(cg->treeContext.tryCount);
5339 196291 if (size <= cg->tryNoteSpace)
5340 196257 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 34 if (!cg->tryBase) {
5351 34 size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK));
5352 34 JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);
5353 34 if (!cg->tryBase)
5354 0 return JS_FALSE;
5355 34 cg->tryNoteSpace = size;
5356 34 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 34 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 34 {
5375 JSTryNote *tn;
5376
5377 JS_ASSERT(cg->tryBase <= cg->tryNext);
5378 JS_ASSERT(catchStart >= 0);
5379 34 tn = cg->tryNext++;
5380 34 tn->start = start;
5381 34 tn->length = end - start;
5382 34 tn->catchStart = catchStart;
5383 34 return tn;
5384 }
5385
5386 void
5387 js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
5388 34 {
5389 uintN count;
5390
5391 34 count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote);
5392 34 if (!count)
5393 34 return;
5394
5395 34 memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count));
5396 34 notes[count].start = 0;
5397 34 notes[count].length = CG_OFFSET(cg);
5398 34 notes[count].catchStart = 0;