1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=99:
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 : #ifdef HAVE_MEMORY_H
45 : #include <memory.h>
46 : #endif
47 : #include <new>
48 : #include <string.h>
49 :
50 : #include "jstypes.h"
51 : #include "jsutil.h"
52 : #include "jsprf.h"
53 : #include "jsapi.h"
54 : #include "jsatom.h"
55 : #include "jsbool.h"
56 : #include "jscntxt.h"
57 : #include "jsversion.h"
58 : #include "jsfun.h"
59 : #include "jsnum.h"
60 : #include "jsopcode.h"
61 : #include "jsscope.h"
62 : #include "jsscript.h"
63 : #include "jsautooplen.h" // generated headers last
64 :
65 : #include "ds/LifoAlloc.h"
66 : #include "frontend/BytecodeCompiler.h"
67 : #include "frontend/BytecodeEmitter.h"
68 : #include "frontend/Parser.h"
69 : #include "frontend/TokenStream.h"
70 : #include "vm/RegExpObject.h"
71 :
72 : #include "jsatominlines.h"
73 : #include "jsscopeinlines.h"
74 : #include "jsscriptinlines.h"
75 :
76 : #include "frontend/BytecodeEmitter-inl.h"
77 : #include "frontend/ParseMaps-inl.h"
78 :
79 : /* Allocation chunk counts, must be powers of two in general. */
80 : #define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */
81 : #define SRCNOTE_CHUNK_LENGTH 1024 /* initial srcnote chunk length */
82 :
83 : /* Macros to compute byte sizes from typed element counts. */
84 : #define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode))
85 : #define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote))
86 :
87 : using namespace js;
88 : using namespace js::gc;
89 : using namespace js::frontend;
90 :
91 : static JSBool
92 : NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, unsigned stackDepth,
93 : size_t start, size_t end);
94 :
95 : static JSBool
96 : SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset);
97 :
98 : void
99 0 : TreeContext::trace(JSTracer *trc)
100 : {
101 0 : bindings.trace(trc);
102 0 : }
103 :
104 1109714 : BytecodeEmitter::BytecodeEmitter(Parser *parser, unsigned lineno)
105 : : TreeContext(parser),
106 : atomIndices(parser->context),
107 : stackDepth(0), maxStackDepth(0),
108 : ntrynotes(0), lastTryNode(NULL),
109 : arrayCompDepth(0),
110 : emitLevel(0),
111 : constMap(parser->context),
112 : constList(parser->context),
113 : upvarIndices(parser->context),
114 : upvarMap(parser->context),
115 : globalScope(NULL),
116 : globalUses(parser->context),
117 : globalMap(parser->context),
118 : closedArgs(parser->context),
119 : closedVars(parser->context),
120 1109714 : typesetCount(0)
121 : {
122 1109714 : flags = TCF_COMPILING;
123 1109714 : memset(&prolog, 0, sizeof prolog);
124 1109714 : memset(&main, 0, sizeof main);
125 1109714 : current = &main;
126 1109714 : firstLine = prolog.currentLine = main.currentLine = lineno;
127 1109714 : }
128 :
129 : bool
130 1109714 : BytecodeEmitter::init(JSContext *cx, TreeContext::InitBehavior ib)
131 : {
132 1109714 : roLexdeps.init();
133 1109714 : return TreeContext::init(cx, ib) && constMap.init() && atomIndices.ensureMap(cx);
134 : }
135 :
136 2219428 : BytecodeEmitter::~BytecodeEmitter()
137 : {
138 1109714 : JSContext *cx = parser->context;
139 :
140 1109714 : cx->free_(prolog.base);
141 1109714 : cx->free_(prolog.notes);
142 1109714 : cx->free_(main.base);
143 1109714 : cx->free_(main.notes);
144 1109714 : }
145 :
146 : static ptrdiff_t
147 108146349 : EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
148 : {
149 108146349 : jsbytecode *base = bce->base();
150 : jsbytecode *newbase;
151 108146349 : jsbytecode *next = bce->next();
152 108146349 : jsbytecode *limit = bce->limit();
153 108146349 : ptrdiff_t offset = next - base;
154 108146349 : size_t minlength = offset + delta;
155 :
156 108146349 : if (next + delta > limit) {
157 : size_t newlength;
158 1214957 : if (!base) {
159 1160777 : JS_ASSERT(!next && !limit);
160 1160777 : newlength = BYTECODE_CHUNK_LENGTH;
161 1160777 : if (newlength < minlength) /* make it bigger if necessary */
162 0 : newlength = RoundUpPow2(minlength);
163 1160777 : newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength));
164 : } else {
165 54180 : JS_ASSERT(base <= next && next <= limit);
166 54180 : newlength = (limit - base) * 2;
167 54180 : if (newlength < minlength) /* make it bigger if necessary */
168 0 : newlength = RoundUpPow2(minlength);
169 54180 : newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength));
170 : }
171 1214957 : if (!newbase) {
172 0 : js_ReportOutOfMemory(cx);
173 0 : return -1;
174 : }
175 1214957 : JS_ASSERT(newlength >= size_t(offset + delta));
176 1214957 : bce->current->base = newbase;
177 1214957 : bce->current->limit = newbase + newlength;
178 1214957 : bce->current->next = newbase + offset;
179 : }
180 108146349 : return offset;
181 : }
182 :
183 : static StaticBlockObject &
184 446653 : CurrentBlock(BytecodeEmitter *bce)
185 : {
186 446653 : JS_ASSERT(bce->topStmt->type == STMT_BLOCK || bce->topStmt->type == STMT_SWITCH);
187 446653 : JS_ASSERT(bce->topStmt->blockObj->isStaticBlock());
188 446653 : return *bce->topStmt->blockObj;
189 : }
190 :
191 : static void
192 108146349 : UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
193 : {
194 108146349 : jsbytecode *pc = bce->code(target);
195 108146349 : JSOp op = (JSOp) *pc;
196 108146349 : const JSCodeSpec *cs = &js_CodeSpec[op];
197 :
198 :
199 108146349 : if (cs->format & JOF_TMPSLOT_MASK) {
200 : /*
201 : * An opcode may temporarily consume stack space during execution.
202 : * Account for this in maxStackDepth separately from uses/defs here.
203 : */
204 : unsigned depth = (unsigned) bce->stackDepth +
205 9046404 : ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT);
206 9046404 : if (depth > bce->maxStackDepth)
207 1163625 : bce->maxStackDepth = depth;
208 : }
209 :
210 : /*
211 : * Specially handle any case that would call js_GetIndexFromBytecode since
212 : * it requires a well-formed script. This allows us to safely pass NULL as
213 : * the 'script' parameter.
214 : */
215 : int nuses, ndefs;
216 108146349 : if (op == JSOP_ENTERBLOCK) {
217 366540 : nuses = 0;
218 366540 : ndefs = CurrentBlock(bce).slotCount();
219 107779809 : } else if (op == JSOP_ENTERLET0) {
220 45212 : nuses = ndefs = CurrentBlock(bce).slotCount();
221 107734597 : } else if (op == JSOP_ENTERLET1) {
222 34901 : nuses = ndefs = CurrentBlock(bce).slotCount() + 1;
223 : } else {
224 107699696 : nuses = StackUses(NULL, pc);
225 107699696 : ndefs = StackDefs(NULL, pc);
226 : }
227 :
228 108146349 : bce->stackDepth -= nuses;
229 108146349 : JS_ASSERT(bce->stackDepth >= 0);
230 108146349 : bce->stackDepth += ndefs;
231 108146349 : if ((unsigned)bce->stackDepth > bce->maxStackDepth)
232 3144375 : bce->maxStackDepth = bce->stackDepth;
233 108146349 : }
234 :
235 : static inline void
236 1666489 : UpdateDecomposeLength(BytecodeEmitter *bce, unsigned start)
237 : {
238 1666489 : unsigned end = bce->offset();
239 1666489 : JS_ASSERT(unsigned(end - start) < 256);
240 1666489 : bce->code(start)[-1] = end - start;
241 1666489 : }
242 :
243 : ptrdiff_t
244 51429296 : frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op)
245 : {
246 51429296 : JS_ASSERT_IF(op == JSOP_ARGUMENTS, !bce->mayOverwriteArguments());
247 :
248 51429296 : ptrdiff_t offset = EmitCheck(cx, bce, 1);
249 :
250 51429296 : if (offset >= 0) {
251 51429296 : *bce->current->next++ = (jsbytecode)op;
252 51429296 : UpdateDepth(cx, bce, offset);
253 : }
254 51429296 : return offset;
255 : }
256 :
257 : ptrdiff_t
258 6509118 : frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
259 : {
260 6509118 : ptrdiff_t offset = EmitCheck(cx, bce, 2);
261 :
262 6509118 : if (offset >= 0) {
263 6509118 : jsbytecode *next = bce->next();
264 6509118 : next[0] = (jsbytecode)op;
265 6509118 : next[1] = op1;
266 6509118 : bce->current->next = next + 2;
267 6509118 : UpdateDepth(cx, bce, offset);
268 : }
269 6509118 : return offset;
270 : }
271 :
272 : ptrdiff_t
273 18217419 : frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
274 : jsbytecode op2)
275 : {
276 18217419 : ptrdiff_t offset = EmitCheck(cx, bce, 3);
277 :
278 18217419 : if (offset >= 0) {
279 18217419 : jsbytecode *next = bce->next();
280 18217419 : next[0] = (jsbytecode)op;
281 18217419 : next[1] = op1;
282 18217419 : next[2] = op2;
283 18217419 : bce->current->next = next + 3;
284 18217419 : UpdateDepth(cx, bce, offset);
285 : }
286 18217419 : return offset;
287 : }
288 :
289 : ptrdiff_t
290 273968 : frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
291 : {
292 273968 : ptrdiff_t length = 1 + (ptrdiff_t)extra;
293 273968 : ptrdiff_t offset = EmitCheck(cx, bce, length);
294 :
295 273968 : if (offset >= 0) {
296 273968 : jsbytecode *next = bce->next();
297 273968 : *next = (jsbytecode)op;
298 273968 : memset(next + 1, 0, BYTECODE_SIZE(extra));
299 273968 : bce->current->next = next + length;
300 :
301 : /*
302 : * Don't UpdateDepth if op's use-count comes from the immediate
303 : * operand yet to be stored in the extra bytes after op.
304 : */
305 273968 : if (js_CodeSpec[op].nuses >= 0)
306 273968 : UpdateDepth(cx, bce, offset);
307 : }
308 273968 : return offset;
309 : }
310 :
311 : static ptrdiff_t
312 2490713 : EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
313 : {
314 2490713 : ptrdiff_t offset = EmitCheck(cx, bce, 5);
315 :
316 2490713 : if (offset >= 0) {
317 2490713 : jsbytecode *next = bce->next();
318 2490713 : next[0] = (jsbytecode)op;
319 2490713 : SET_JUMP_OFFSET(next, off);
320 2490713 : bce->current->next = next + 5;
321 2490713 : UpdateDepth(cx, bce, offset);
322 : }
323 2490713 : return offset;
324 : }
325 :
326 : /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
327 : const char js_with_statement_str[] = "with statement";
328 : const char js_finally_block_str[] = "finally block";
329 : const char js_script_str[] = "script";
330 :
331 : static const char *statementName[] = {
332 : "label statement", /* LABEL */
333 : "if statement", /* IF */
334 : "else statement", /* ELSE */
335 : "destructuring body", /* BODY */
336 : "switch statement", /* SWITCH */
337 : "block", /* BLOCK */
338 : js_with_statement_str, /* WITH */
339 : "catch block", /* CATCH */
340 : "try block", /* TRY */
341 : js_finally_block_str, /* FINALLY */
342 : js_finally_block_str, /* SUBROUTINE */
343 : "do loop", /* DO_LOOP */
344 : "for loop", /* FOR_LOOP */
345 : "for/in loop", /* FOR_IN_LOOP */
346 : "while loop", /* WHILE_LOOP */
347 : };
348 :
349 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(statementName) == STMT_LIMIT);
350 :
351 : static const char *
352 0 : StatementName(BytecodeEmitter *bce)
353 : {
354 0 : if (!bce->topStmt)
355 0 : return js_script_str;
356 0 : return statementName[bce->topStmt->type];
357 : }
358 :
359 : static void
360 0 : ReportStatementTooLarge(JSContext *cx, BytecodeEmitter *bce)
361 : {
362 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
363 0 : StatementName(bce));
364 0 : }
365 :
366 : bool
367 13838 : TreeContext::inStatement(StmtType type)
368 : {
369 31595 : for (StmtInfo *stmt = topStmt; stmt; stmt = stmt->down) {
370 17757 : if (stmt->type == type)
371 0 : return true;
372 : }
373 13838 : return false;
374 : }
375 :
376 : bool
377 0 : TreeContext::skipSpansGenerator(unsigned skip)
378 : {
379 0 : TreeContext *tc = this;
380 0 : for (unsigned i = 0; i < skip; ++i, tc = tc->parent) {
381 0 : if (!tc)
382 0 : return false;
383 0 : if (tc->flags & TCF_FUN_IS_GENERATOR)
384 0 : return true;
385 : }
386 0 : return false;
387 : }
388 :
389 : bool
390 1101840 : frontend::SetStaticLevel(TreeContext *tc, unsigned staticLevel)
391 : {
392 : /*
393 : * This is a lot simpler than error-checking every UpvarCookie::set, and
394 : * practically speaking it leaves more than enough room for upvars.
395 : */
396 1101840 : if (UpvarCookie::isLevelReserved(staticLevel)) {
397 : JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
398 0 : JSMSG_TOO_DEEP, js_function_str);
399 0 : return false;
400 : }
401 1101840 : tc->staticLevel = staticLevel;
402 1101840 : return true;
403 : }
404 :
405 : bool
406 2568619 : frontend::GenerateBlockId(TreeContext *tc, uint32_t &blockid)
407 : {
408 2568619 : if (tc->blockidGen == JS_BIT(20)) {
409 : JS_ReportErrorNumber(tc->parser->context, js_GetErrorMessage, NULL,
410 0 : JSMSG_NEED_DIET, "program");
411 0 : return false;
412 : }
413 2568619 : blockid = tc->blockidGen++;
414 2568619 : return true;
415 : }
416 :
417 : void
418 7762704 : frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top)
419 : {
420 7762704 : stmt->type = type;
421 7762704 : stmt->flags = 0;
422 7762704 : stmt->blockid = tc->blockid();
423 7762704 : SET_STATEMENT_TOP(stmt, top);
424 7762704 : stmt->label = NULL;
425 7762704 : JS_ASSERT(!stmt->blockObj);
426 7762704 : stmt->down = tc->topStmt;
427 7762704 : tc->topStmt = stmt;
428 7762704 : if (STMT_LINKS_SCOPE(stmt)) {
429 1323 : stmt->downScope = tc->topScopeStmt;
430 1323 : tc->topScopeStmt = stmt;
431 : } else {
432 7761381 : stmt->downScope = NULL;
433 : }
434 7762704 : }
435 :
436 : void
437 763163 : frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top)
438 : {
439 763163 : PushStatement(tc, stmt, STMT_BLOCK, top);
440 763163 : stmt->flags |= SIF_SCOPE;
441 763163 : blockObj.setEnclosingBlock(tc->blockChain);
442 763163 : stmt->downScope = tc->topScopeStmt;
443 763163 : tc->topScopeStmt = stmt;
444 763163 : tc->blockChain = &blockObj;
445 763163 : stmt->blockObj = &blockObj;
446 763163 : }
447 :
448 : /*
449 : * Emit a backpatch op with offset pointing to the previous jump of this type,
450 : * so that we can walk back up the chain fixing up the op and jump offset.
451 : */
452 : static ptrdiff_t
453 749589 : EmitBackPatchOp(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t *lastp)
454 : {
455 : ptrdiff_t offset, delta;
456 :
457 749589 : offset = bce->offset();
458 749589 : delta = offset - *lastp;
459 749589 : *lastp = offset;
460 749589 : JS_ASSERT(delta > 0);
461 749589 : return EmitJump(cx, bce, op, delta);
462 : }
463 :
464 : /* A macro for inlining at the top of EmitTree (whence it came). */
465 : #define UPDATE_LINE_NUMBER_NOTES(cx, bce, line) \
466 : JS_BEGIN_MACRO \
467 : unsigned line_ = (line); \
468 : unsigned delta_ = line_ - bce->currentLine(); \
469 : if (delta_ != 0) { \
470 : /* \
471 : * Encode any change in the current source line number by using \
472 : * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \
473 : * whichever consumes less space. \
474 : * \
475 : * NB: We handle backward line number deltas (possible with for \
476 : * loops where the update part is emitted after the body, but its \
477 : * line number is <= any line number in the body) here by letting \
478 : * unsigned delta_ wrap to a very large number, which triggers a \
479 : * SRC_SETLINE. \
480 : */ \
481 : bce->current->currentLine = line_; \
482 : if (delta_ >= (unsigned)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \
483 : if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)line_) < 0) \
484 : return JS_FALSE; \
485 : } else { \
486 : do { \
487 : if (NewSrcNote(cx, bce, SRC_NEWLINE) < 0) \
488 : return JS_FALSE; \
489 : } while (--delta_ != 0); \
490 : } \
491 : } \
492 : JS_END_MACRO
493 :
494 : /* A function, so that we avoid macro-bloating all the other callsites. */
495 : static JSBool
496 2571470 : UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, unsigned line)
497 : {
498 2571470 : UPDATE_LINE_NUMBER_NOTES(cx, bce, line);
499 2571470 : return JS_TRUE;
500 : }
501 :
502 : static ptrdiff_t
503 174187 : EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
504 : {
505 174187 : if (nextpn) {
506 : /*
507 : * Try to give the JSOP_LOOPHEAD the same line number as the next
508 : * instruction. nextpn is often a block, in which case the next
509 : * instruction typically comes from the first statement inside.
510 : */
511 119571 : JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
512 119571 : if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
513 65084 : nextpn = nextpn->pn_head;
514 119571 : if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
515 0 : return -1;
516 : }
517 :
518 174187 : return Emit1(cx, bce, JSOP_LOOPHEAD);
519 : }
520 :
521 : static bool
522 174187 : EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
523 : {
524 174187 : if (nextpn) {
525 : /* Update the line number, as for LOOPHEAD. */
526 119125 : JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
527 119125 : if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
528 1095 : nextpn = nextpn->pn_head;
529 119125 : if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
530 0 : return false;
531 : }
532 :
533 174187 : return Emit1(cx, bce, JSOP_LOOPENTRY) >= 0;
534 : }
535 :
536 : /*
537 : * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
538 : * a type set to store its result.
539 : */
540 : static inline void
541 52892002 : CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op)
542 : {
543 52892002 : if (js_CodeSpec[op].format & JOF_TYPESET) {
544 23300301 : if (bce->typesetCount < UINT16_MAX)
545 23300301 : bce->typesetCount++;
546 : }
547 52892002 : }
548 :
549 : /*
550 : * Macro to emit a bytecode followed by a uint16_t immediate operand stored in
551 : * big-endian order, used for arg and var numbers as well as for atomIndexes.
552 : * NB: We use cx and bce from our caller's lexical environment, and return
553 : * false on error.
554 : */
555 : #define EMIT_UINT16_IMM_OP(op, i) \
556 : JS_BEGIN_MACRO \
557 : if (Emit3(cx, bce, op, UINT16_HI(i), UINT16_LO(i)) < 0) \
558 : return JS_FALSE; \
559 : CheckTypeSet(cx, bce, op); \
560 : JS_END_MACRO
561 :
562 : #define EMIT_UINT16PAIR_IMM_OP(op, i, j) \
563 : JS_BEGIN_MACRO \
564 : ptrdiff_t off_ = EmitN(cx, bce, op, 2 * UINT16_LEN); \
565 : if (off_ < 0) \
566 : return JS_FALSE; \
567 : jsbytecode *pc_ = bce->code(off_); \
568 : SET_UINT16(pc_, i); \
569 : pc_ += UINT16_LEN; \
570 : SET_UINT16(pc_, j); \
571 : JS_END_MACRO
572 :
573 : #define EMIT_UINT16_IN_PLACE(offset, op, i) \
574 : JS_BEGIN_MACRO \
575 : bce->code(offset)[0] = op; \
576 : bce->code(offset)[1] = UINT16_HI(i); \
577 : bce->code(offset)[2] = UINT16_LO(i); \
578 : JS_END_MACRO
579 :
580 : #define EMIT_UINT32_IN_PLACE(offset, op, i) \
581 : JS_BEGIN_MACRO \
582 : bce->code(offset)[0] = op; \
583 : bce->code(offset)[1] = jsbytecode(i >> 24); \
584 : bce->code(offset)[2] = jsbytecode(i >> 16); \
585 : bce->code(offset)[3] = jsbytecode(i >> 8); \
586 : bce->code(offset)[4] = jsbytecode(i); \
587 : JS_END_MACRO
588 :
589 : static JSBool
590 18 : FlushPops(JSContext *cx, BytecodeEmitter *bce, int *npops)
591 : {
592 18 : JS_ASSERT(*npops != 0);
593 18 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
594 0 : return JS_FALSE;
595 18 : EMIT_UINT16_IMM_OP(JSOP_POPN, *npops);
596 18 : *npops = 0;
597 18 : return JS_TRUE;
598 : }
599 :
600 : static bool
601 6837 : PopIterator(JSContext *cx, BytecodeEmitter *bce)
602 : {
603 6837 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
604 0 : return false;
605 6837 : if (Emit1(cx, bce, JSOP_ENDITER) < 0)
606 0 : return false;
607 6837 : return true;
608 : }
609 :
610 : /*
611 : * Emit additional bytecode(s) for non-local jumps.
612 : */
613 : static JSBool
614 908329 : EmitNonLocalJumpFixup(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt)
615 : {
616 : /*
617 : * The non-local jump fixup we emit will unbalance bce->stackDepth, because
618 : * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
619 : * end of a with statement, so we save bce->stackDepth here and restore it
620 : * just before a successful return.
621 : */
622 908329 : int depth = bce->stackDepth;
623 908329 : int npops = 0;
624 :
625 : #define FLUSH_POPS() if (npops && !FlushPops(cx, bce, &npops)) return JS_FALSE
626 :
627 2498312 : for (StmtInfo *stmt = bce->topStmt; stmt != toStmt; stmt = stmt->down) {
628 1595958 : switch (stmt->type) {
629 : case STMT_FINALLY:
630 7163 : FLUSH_POPS();
631 7163 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
632 0 : return JS_FALSE;
633 7163 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &GOSUBS(*stmt)) < 0)
634 0 : return JS_FALSE;
635 7163 : break;
636 :
637 : case STMT_WITH:
638 : /* There's a With object on the stack that we need to pop. */
639 54 : FLUSH_POPS();
640 54 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
641 0 : return JS_FALSE;
642 54 : if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
643 0 : return JS_FALSE;
644 54 : break;
645 :
646 : case STMT_FOR_IN_LOOP:
647 3274 : FLUSH_POPS();
648 3274 : if (!PopIterator(cx, bce))
649 0 : return JS_FALSE;
650 3274 : break;
651 :
652 : case STMT_SUBROUTINE:
653 : /*
654 : * There's a [exception or hole, retsub pc-index] pair on the
655 : * stack that we need to pop.
656 : */
657 18 : npops += 2;
658 18 : break;
659 :
660 : default:;
661 : }
662 :
663 1595958 : if (stmt->flags & SIF_SCOPE) {
664 122315 : FLUSH_POPS();
665 122315 : unsigned blockObjCount = stmt->blockObj->slotCount();
666 122315 : if (stmt->flags & SIF_FOR_BLOCK) {
667 : /*
668 : * For a for-let-in statement, pushing/popping the block is
669 : * interleaved with JSOP_(END)ITER. Just handle both together
670 : * here and skip over the enclosing STMT_FOR_IN_LOOP.
671 : */
672 9538 : JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP);
673 9538 : stmt = stmt->down;
674 9538 : if (stmt == toStmt)
675 5975 : break;
676 3563 : if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
677 0 : return JS_FALSE;
678 3563 : if (!PopIterator(cx, bce))
679 0 : return JS_FALSE;
680 3563 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
681 0 : return JS_FALSE;
682 3563 : EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
683 : } else {
684 : /* There is a Block object with locals on the stack to pop. */
685 112777 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
686 0 : return JS_FALSE;
687 112777 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
688 : }
689 : }
690 : }
691 :
692 908329 : FLUSH_POPS();
693 908329 : bce->stackDepth = depth;
694 908329 : return JS_TRUE;
695 :
696 : #undef FLUSH_POPS
697 : }
698 :
699 : static const jsatomid INVALID_ATOMID = -1;
700 :
701 : static ptrdiff_t
702 248243 : EmitGoto(JSContext *cx, BytecodeEmitter *bce, StmtInfo *toStmt, ptrdiff_t *lastp,
703 : jsatomid labelIndex = INVALID_ATOMID, SrcNoteType noteType = SRC_NULL)
704 : {
705 : int index;
706 :
707 248243 : if (!EmitNonLocalJumpFixup(cx, bce, toStmt))
708 0 : return -1;
709 :
710 248243 : if (labelIndex != INVALID_ATOMID)
711 136 : index = NewSrcNote2(cx, bce, noteType, ptrdiff_t(labelIndex));
712 248107 : else if (noteType != SRC_NULL)
713 91495 : index = NewSrcNote(cx, bce, noteType);
714 : else
715 156612 : index = 0;
716 248243 : if (index < 0)
717 0 : return -1;
718 :
719 248243 : return EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, lastp);
720 : }
721 :
722 : static JSBool
723 8196244 : BackPatch(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode *target, jsbytecode op)
724 : {
725 : jsbytecode *pc, *stop;
726 : ptrdiff_t delta, span;
727 :
728 8196244 : pc = bce->code(last);
729 8196244 : stop = bce->code(-1);
730 17142077 : while (pc != stop) {
731 749589 : delta = GET_JUMP_OFFSET(pc);
732 749589 : span = target - pc;
733 749589 : SET_JUMP_OFFSET(pc, span);
734 749589 : *pc = op;
735 749589 : pc -= delta;
736 : }
737 8196244 : return JS_TRUE;
738 : }
739 :
740 : void
741 7761928 : frontend::PopStatementTC(TreeContext *tc)
742 : {
743 7761928 : StmtInfo *stmt = tc->topStmt;
744 7761928 : tc->topStmt = stmt->down;
745 7761928 : if (STMT_LINKS_SCOPE(stmt)) {
746 896899 : tc->topScopeStmt = stmt->downScope;
747 896899 : if (stmt->flags & SIF_SCOPE)
748 895576 : tc->blockChain = stmt->blockObj->enclosingBlock();
749 : }
750 7761928 : }
751 :
752 : JSBool
753 4207606 : frontend::PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
754 : {
755 4207606 : StmtInfo *stmt = bce->topStmt;
756 12140776 : if (!STMT_IS_TRYING(stmt) &&
757 3966585 : (!BackPatch(cx, bce, stmt->breaks, bce->next(), JSOP_GOTO) ||
758 3966585 : !BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
759 : {
760 0 : return JS_FALSE;
761 : }
762 4207606 : PopStatementTC(bce);
763 4207606 : return JS_TRUE;
764 : }
765 :
766 : JSBool
767 168764 : frontend::DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, ParseNode *pn)
768 : {
769 : /* XXX just do numbers for now */
770 168764 : if (pn->isKind(PNK_NUMBER)) {
771 7973 : if (!bce->constMap.put(atom, NumberValue(pn->pn_dval)))
772 0 : return JS_FALSE;
773 : }
774 168764 : return JS_TRUE;
775 : }
776 :
777 : StmtInfo *
778 14987965 : frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, int *slotp, StmtInfo *stmt)
779 : {
780 14987965 : if (!stmt)
781 14987965 : stmt = tc->topScopeStmt;
782 17216708 : for (; stmt; stmt = stmt->downScope) {
783 3128904 : if (stmt->type == STMT_WITH)
784 783 : break;
785 :
786 : /* Skip "maybe scope" statements that don't contain let bindings. */
787 3128121 : if (!(stmt->flags & SIF_SCOPE))
788 0 : continue;
789 :
790 3128121 : StaticBlockObject &blockObj = *stmt->blockObj;
791 3128121 : const Shape *shape = blockObj.nativeLookup(tc->parser->context, ATOM_TO_JSID(atom));
792 3128121 : if (shape) {
793 899378 : JS_ASSERT(shape->hasShortID());
794 :
795 899378 : if (slotp)
796 0 : *slotp = blockObj.stackDepth() + shape->shortid();
797 899378 : return stmt;
798 : }
799 : }
800 :
801 14088587 : if (slotp)
802 0 : *slotp = -1;
803 14088587 : return stmt;
804 : }
805 :
806 : /*
807 : * The function sets vp to NO_CONSTANT when the atom does not corresponds to a
808 : * name defining a constant.
809 : */
810 : static JSBool
811 2360 : LookupCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, Value *constp)
812 : {
813 : /*
814 : * Chase down the bce stack, but only until we reach the outermost bce.
815 : * This enables propagating consts from top-level into switch cases in a
816 : * function compiled along with the top-level script.
817 : */
818 2360 : constp->setMagic(JS_NO_CONSTANT);
819 7006 : do {
820 4697 : if (bce->inFunction() || bce->compileAndGo()) {
821 : /* XXX this will need revising if 'const' becomes block-scoped. */
822 2419 : StmtInfo *stmt = LexicalLookup(bce, atom, NULL);
823 2419 : if (stmt)
824 0 : return JS_TRUE;
825 :
826 2419 : if (BytecodeEmitter::ConstMap::Ptr p = bce->constMap.lookup(atom)) {
827 9 : JS_ASSERT(!p->value.isMagic(JS_NO_CONSTANT));
828 9 : *constp = p->value;
829 9 : return JS_TRUE;
830 : }
831 :
832 : /*
833 : * Try looking in the variable object for a direct property that
834 : * is readonly and permanent. We know such a property can't be
835 : * shadowed by another property on obj's prototype chain, or a
836 : * with object or catch variable; nor can prop's value be changed,
837 : * nor can prop be deleted.
838 : */
839 2410 : if (bce->inFunction()) {
840 2356 : if (bce->bindings.hasBinding(cx, atom))
841 10 : break;
842 : } else {
843 54 : JS_ASSERT(bce->compileAndGo());
844 54 : JSObject *obj = bce->scopeChain();
845 :
846 54 : const Shape *shape = obj->nativeLookup(cx, ATOM_TO_JSID(atom));
847 54 : if (shape) {
848 : /*
849 : * We're compiling code that will be executed immediately,
850 : * not re-executed against a different scope chain and/or
851 : * variable object. Therefore we can get constant values
852 : * from our variable object here.
853 : */
854 27 : if (!shape->writable() && !shape->configurable() &&
855 18 : shape->hasDefaultGetter() && shape->hasSlot()) {
856 9 : *constp = obj->nativeGetSlot(shape->slot());
857 : }
858 : }
859 :
860 54 : if (shape)
861 9 : break;
862 : }
863 : }
864 2337 : } while (bce->parent && (bce = bce->parent->asBytecodeEmitter()));
865 2351 : return JS_TRUE;
866 : }
867 :
868 : static bool
869 2589295 : EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
870 : {
871 2589295 : const size_t len = 1 + UINT32_INDEX_LEN;
872 2589295 : JS_ASSERT(len == size_t(js_CodeSpec[op].length));
873 2589295 : ptrdiff_t offset = EmitCheck(cx, bce, len);
874 2589295 : if (offset < 0)
875 0 : return false;
876 :
877 2589295 : jsbytecode *next = bce->next();
878 2589295 : next[0] = jsbytecode(op);
879 2589295 : SET_UINT32_INDEX(next, index);
880 2589295 : bce->current->next = next + len;
881 2589295 : UpdateDepth(cx, bce, offset);
882 2589295 : CheckTypeSet(cx, bce, op);
883 2589295 : return true;
884 : }
885 :
886 : static bool
887 26386629 : EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
888 : {
889 26386629 : const size_t len = js_CodeSpec[op].length;
890 26386629 : JS_ASSERT(len >= 1 + UINT32_INDEX_LEN);
891 26386629 : ptrdiff_t offset = EmitCheck(cx, bce, len);
892 26386629 : if (offset < 0)
893 0 : return false;
894 :
895 26386629 : jsbytecode *next = bce->next();
896 26386629 : next[0] = jsbytecode(op);
897 26386629 : SET_UINT32_INDEX(next, index);
898 26386629 : bce->current->next = next + len;
899 26386629 : UpdateDepth(cx, bce, offset);
900 26386629 : CheckTypeSet(cx, bce, op);
901 26386629 : return true;
902 : }
903 :
904 : static bool
905 25022127 : EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
906 : {
907 25022127 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
908 :
909 25022127 : if (op == JSOP_GETPROP && atom == cx->runtime->atomState.lengthAtom) {
910 : /* Specialize length accesses for the interpreter. */
911 163185 : op = JSOP_LENGTH;
912 : }
913 :
914 : jsatomid index;
915 25022127 : if (!bce->makeAtomIndex(atom, &index))
916 0 : return false;
917 :
918 25022127 : return EmitIndexOp(cx, op, index, bce);
919 : }
920 :
921 : static bool
922 25022010 : EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
923 : {
924 25022010 : JS_ASSERT(pn->pn_atom != NULL);
925 25022010 : return EmitAtomOp(cx, pn->pn_atom, op, bce);
926 : }
927 :
928 : static bool
929 22755 : EmitAtomIncDec(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
930 : {
931 22755 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
932 22755 : JS_ASSERT(js_CodeSpec[op].format & (JOF_INC | JOF_DEC));
933 :
934 : jsatomid index;
935 22755 : if (!bce->makeAtomIndex(atom, &index))
936 0 : return false;
937 :
938 22755 : const size_t len = 1 + UINT32_INDEX_LEN + 1;
939 22755 : JS_ASSERT(size_t(js_CodeSpec[op].length) == len);
940 22755 : ptrdiff_t offset = EmitCheck(cx, bce, len);
941 22755 : if (offset < 0)
942 0 : return false;
943 :
944 22755 : jsbytecode *next = bce->next();
945 22755 : next[0] = jsbytecode(op);
946 22755 : SET_UINT32_INDEX(next, index);
947 22755 : bce->current->next = next + len;
948 22755 : UpdateDepth(cx, bce, offset);
949 22755 : CheckTypeSet(cx, bce, op);
950 22755 : return true;
951 : }
952 :
953 : static bool
954 915364 : EmitFunctionOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
955 : {
956 915364 : return EmitIndex32(cx, op, index, bce);
957 : }
958 :
959 : static bool
960 453929 : EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
961 : {
962 453929 : JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
963 453929 : return EmitIndex32(cx, op, bce->objectList.index(objbox), bce);
964 : }
965 :
966 : static bool
967 34050 : EmitRegExp(JSContext *cx, uint32_t index, BytecodeEmitter *bce)
968 : {
969 34050 : return EmitIndex32(cx, JSOP_REGEXP, index, bce);
970 : }
971 :
972 : static bool
973 40990 : EmitSlotObjectOp(JSContext *cx, JSOp op, unsigned slot, uint32_t index, BytecodeEmitter *bce)
974 : {
975 40990 : JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTOBJECT);
976 :
977 40990 : ptrdiff_t off = EmitN(cx, bce, op, SLOTNO_LEN + UINT32_INDEX_LEN);
978 40990 : if (off < 0)
979 0 : return false;
980 :
981 40990 : jsbytecode *pc = bce->code(off);
982 40990 : SET_SLOTNO(pc, slot);
983 40990 : pc += SLOTNO_LEN;
984 40990 : SET_UINT32_INDEX(pc, index);
985 40990 : return true;
986 : }
987 :
988 : static bool
989 13766 : EmitArguments(JSContext *cx, BytecodeEmitter *bce)
990 : {
991 13766 : if (!bce->mayOverwriteArguments())
992 13649 : return Emit1(cx, bce, JSOP_ARGUMENTS) >= 0;
993 117 : return EmitAtomOp(cx, cx->runtime->atomState.argumentsAtom, JSOP_NAME, bce);
994 : }
995 :
996 : bool
997 2076395 : BytecodeEmitter::shouldNoteClosedName(ParseNode *pn)
998 : {
999 2076395 : return !callsEval() && pn->isDefn() && pn->isClosed();
1000 : }
1001 :
1002 : /*
1003 : * Adjust the slot for a block local to account for the number of variables
1004 : * that share the same index space with locals. Due to the incremental code
1005 : * generation for top-level script, we do the adjustment via code patching in
1006 : * js::frontend::CompileScript; see comments there.
1007 : *
1008 : * The function returns -1 on failures.
1009 : */
1010 : static int
1011 460642 : AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
1012 : {
1013 460642 : JS_ASSERT((unsigned) slot < bce->maxStackDepth);
1014 460642 : if (bce->inFunction()) {
1015 425594 : slot += bce->bindings.countVars();
1016 425594 : if ((unsigned) slot >= SLOTNO_LIMIT) {
1017 : ReportCompileErrorNumber(cx, bce->tokenStream(), NULL, JSREPORT_ERROR,
1018 0 : JSMSG_TOO_MANY_LOCALS);
1019 0 : slot = -1;
1020 : }
1021 : }
1022 460642 : return slot;
1023 : }
1024 :
1025 : static bool
1026 446653 : EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
1027 : {
1028 446653 : JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
1029 446653 : if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
1030 0 : return false;
1031 :
1032 446653 : StaticBlockObject &blockObj = pn->pn_objbox->object->asStaticBlock();
1033 :
1034 : int depth = bce->stackDepth -
1035 446653 : (blockObj.slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
1036 446653 : JS_ASSERT(depth >= 0);
1037 :
1038 446653 : blockObj.setStackDepth(depth);
1039 :
1040 446653 : int depthPlusFixed = AdjustBlockSlot(cx, bce, depth);
1041 446653 : if (depthPlusFixed < 0)
1042 0 : return false;
1043 :
1044 1198996 : for (unsigned i = 0; i < blockObj.slotCount(); i++) {
1045 752343 : Definition *dn = blockObj.maybeDefinitionParseNode(i);
1046 752343 : blockObj.poisonDefinitionParseNode(i);
1047 :
1048 : /* Beware the empty destructuring dummy. */
1049 752343 : if (!dn) {
1050 3942 : JS_ASSERT(i + 1 <= blockObj.slotCount());
1051 3942 : continue;
1052 : }
1053 :
1054 748401 : JS_ASSERT(dn->isDefn());
1055 748401 : JS_ASSERT(unsigned(dn->frameSlot() + depthPlusFixed) < JS_BIT(16));
1056 748401 : dn->pn_cookie.set(dn->pn_cookie.level(), uint16_t(dn->frameSlot() + depthPlusFixed));
1057 : #ifdef DEBUG
1058 1788213 : for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1059 1039812 : JS_ASSERT(pnu->pn_lexdef == dn);
1060 1039812 : JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
1061 1039812 : JS_ASSERT(pnu->pn_cookie.isFree());
1062 : }
1063 : #endif
1064 : }
1065 :
1066 : /*
1067 : * If clones of this block will have any extensible parents, then the
1068 : * clones must get unique shapes; see the comments for
1069 : * js::Bindings::extensibleParents.
1070 : */
1071 887450 : if ((bce->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
1072 440797 : bce->bindings.extensibleParents()) {
1073 8532 : Shape *newShape = Shape::setExtensibleParents(cx, blockObj.lastProperty());
1074 8532 : if (!newShape)
1075 0 : return false;
1076 8532 : blockObj.setLastPropertyInfallible(newShape);
1077 : }
1078 :
1079 446653 : return true;
1080 : }
1081 :
1082 : /*
1083 : * Try to convert a *NAME op to a *GNAME op, which optimizes access to
1084 : * undeclared globals. Return true if a conversion was made.
1085 : *
1086 : * This conversion is not made if we are in strict mode. In eval code nested
1087 : * within (strict mode) eval code, access to an undeclared "global" might
1088 : * merely be to a binding local to that outer eval:
1089 : *
1090 : * "use strict";
1091 : * var x = "global";
1092 : * eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
1093 : *
1094 : * Outside eval code, access to an undeclared global is a strict mode error:
1095 : *
1096 : * "use strict";
1097 : * function foo()
1098 : * {
1099 : * undeclared = 17; // throws ReferenceError
1100 : * }
1101 : * foo();
1102 : */
1103 : static bool
1104 12047030 : TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
1105 : {
1106 17729753 : if (bce->compileAndGo() &&
1107 : bce->globalScope->globalObj &&
1108 1895387 : !bce->mightAliasLocals() &&
1109 1893668 : !pn->isDeoptimized() &&
1110 1893668 : !(bce->flags & TCF_STRICT_MODE_CODE)) {
1111 1893050 : switch (*op) {
1112 1722596 : case JSOP_NAME: *op = JSOP_GETGNAME; break;
1113 117819 : case JSOP_SETNAME: *op = JSOP_SETGNAME; break;
1114 1552 : case JSOP_INCNAME: *op = JSOP_INCGNAME; break;
1115 4768 : case JSOP_NAMEINC: *op = JSOP_GNAMEINC; break;
1116 81 : case JSOP_DECNAME: *op = JSOP_DECGNAME; break;
1117 144 : case JSOP_NAMEDEC: *op = JSOP_GNAMEDEC; break;
1118 : case JSOP_SETCONST:
1119 : case JSOP_DELNAME:
1120 : /* Not supported. */
1121 46090 : return false;
1122 0 : default: JS_NOT_REACHED("gname");
1123 : }
1124 1846960 : return true;
1125 : }
1126 10153980 : return false;
1127 : }
1128 :
1129 : // Binds a global, given a |dn| that is known to have the PND_GVAR bit, and a pn
1130 : // that is |dn| or whose definition is |dn|. |pn->pn_cookie| is an outparam
1131 : // that will be free (meaning no binding), or a slot number.
1132 : static bool
1133 24133 : BindKnownGlobal(JSContext *cx, BytecodeEmitter *bce, ParseNode *dn, ParseNode *pn, JSAtom *atom)
1134 : {
1135 : // Cookie is an outparam; make sure caller knew to clear it.
1136 24133 : JS_ASSERT(pn->pn_cookie.isFree());
1137 :
1138 24133 : if (bce->mightAliasLocals())
1139 54 : return true;
1140 :
1141 24079 : GlobalScope *globalScope = bce->globalScope;
1142 :
1143 : jsatomid index;
1144 24079 : if (dn->pn_cookie.isFree()) {
1145 : // The definition wasn't bound, so find its atom's index in the
1146 : // mapping of defined globals.
1147 24079 : AtomIndexPtr p = globalScope->names.lookup(atom);
1148 24079 : JS_ASSERT(!!p);
1149 24079 : index = p.value();
1150 : } else {
1151 0 : BytecodeEmitter *globalbce = globalScope->bce;
1152 :
1153 : // If the definition is bound, and we're in the same bce, we can re-use
1154 : // its cookie.
1155 0 : if (globalbce == bce) {
1156 0 : pn->pn_cookie = dn->pn_cookie;
1157 0 : pn->pn_dflags |= PND_BOUND;
1158 0 : return true;
1159 : }
1160 :
1161 : // Otherwise, find the atom's index by using the originating bce's
1162 : // global use table.
1163 0 : index = globalbce->globalUses[dn->pn_cookie.slot()].slot;
1164 : }
1165 :
1166 24079 : if (!bce->addGlobalUse(atom, index, &pn->pn_cookie))
1167 0 : return false;
1168 :
1169 24079 : if (!pn->pn_cookie.isFree())
1170 24079 : pn->pn_dflags |= PND_BOUND;
1171 :
1172 24079 : return true;
1173 : }
1174 :
1175 : // See BindKnownGlobal()'s comment.
1176 : static bool
1177 207341 : BindGlobal(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSAtom *atom)
1178 : {
1179 207341 : pn->pn_cookie.makeFree();
1180 :
1181 : Definition *dn;
1182 207341 : if (pn->isUsed()) {
1183 21745 : dn = pn->pn_lexdef;
1184 : } else {
1185 185596 : if (!pn->isDefn())
1186 0 : return true;
1187 185596 : dn = (Definition *)pn;
1188 : }
1189 :
1190 : // Only optimize for defined globals.
1191 207341 : if (!dn->isGlobal())
1192 183208 : return true;
1193 :
1194 24133 : return BindKnownGlobal(cx, bce, dn, pn, atom);
1195 : }
1196 :
1197 : /*
1198 : * BindNameToSlot attempts to optimize name gets and sets to stack slot loads
1199 : * and stores, given the compile-time information in bce and a PNK_NAME node pn.
1200 : * It returns false on error, true on success.
1201 : *
1202 : * The caller can inspect pn->pn_cookie for FREE_UPVAR_COOKIE to tell whether
1203 : * optimization occurred, in which case BindNameToSlot also updated pn->pn_op.
1204 : * If pn->pn_cookie is still FREE_UPVAR_COOKIE on return, pn->pn_op still may
1205 : * have been optimized, e.g., from JSOP_NAME to JSOP_CALLEE. Whether or not
1206 : * pn->pn_op was modified, if this function finds an argument or local variable
1207 : * name, PND_CONST will be set in pn_dflags for read-only properties after a
1208 : * successful return.
1209 : *
1210 : * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget
1211 : * to update the special cases in EmitFor (for-in) and EmitAssignment (= and
1212 : * op=, e.g. +=).
1213 : */
1214 : static JSBool
1215 22417247 : BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
1216 : {
1217 : Definition *dn;
1218 : JSOp op;
1219 : JSAtom *atom;
1220 : Definition::Kind dn_kind;
1221 :
1222 22417247 : JS_ASSERT(pn->isKind(PNK_NAME));
1223 :
1224 : /* Idempotency tests come first, since we may be called more than once. */
1225 22417247 : if (pn->pn_dflags & PND_BOUND)
1226 4230559 : return JS_TRUE;
1227 :
1228 : /* No cookie initialized for these two, they're pre-bound by definition. */
1229 18186688 : JS_ASSERT(!pn->isOp(JSOP_ARGUMENTS) && !pn->isOp(JSOP_CALLEE));
1230 :
1231 : /*
1232 : * The parser linked all uses (including forward references) to their
1233 : * definitions, unless a with statement or direct eval intervened.
1234 : */
1235 18186688 : if (pn->isUsed()) {
1236 17873826 : JS_ASSERT(pn->pn_cookie.isFree());
1237 17873826 : dn = pn->pn_lexdef;
1238 17873826 : JS_ASSERT(dn->isDefn());
1239 17873826 : if (pn->isDeoptimized())
1240 16666 : return JS_TRUE;
1241 17857160 : pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
1242 : } else {
1243 312862 : if (!pn->isDefn())
1244 153 : return JS_TRUE;
1245 312709 : dn = (Definition *) pn;
1246 : }
1247 :
1248 18169869 : op = pn->getOp();
1249 18169869 : if (op == JSOP_NOP)
1250 0 : return JS_TRUE;
1251 :
1252 18169869 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
1253 18169869 : atom = pn->pn_atom;
1254 18169869 : UpvarCookie cookie = dn->pn_cookie;
1255 18169869 : dn_kind = dn->kind();
1256 :
1257 : /*
1258 : * Turn attempts to mutate const-declared bindings into get ops (for
1259 : * pre-increment and pre-decrement ops, our caller will have to emit
1260 : * JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
1261 : *
1262 : * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
1263 : * bindings visible to the compiler are permanent in JS unless the
1264 : * declaration originates at top level in eval code.
1265 : */
1266 18169869 : switch (op) {
1267 : case JSOP_NAME:
1268 : case JSOP_SETCONST:
1269 17135688 : break;
1270 : case JSOP_DELNAME:
1271 901 : if (dn_kind != Definition::UNKNOWN) {
1272 18 : if (bce->parser->callerFrame && dn->isTopLevel())
1273 0 : JS_ASSERT(bce->compileAndGo());
1274 : else
1275 18 : pn->setOp(JSOP_FALSE);
1276 18 : pn->pn_dflags |= PND_BOUND;
1277 18 : return JS_TRUE;
1278 : }
1279 883 : break;
1280 : default:
1281 1033280 : if (pn->isConst()) {
1282 9 : if (bce->needStrictChecks()) {
1283 0 : JSAutoByteString name;
1284 0 : if (!js_AtomToPrintableString(cx, atom, &name) ||
1285 : !ReportStrictModeError(cx, bce->tokenStream(), bce, pn, JSMSG_READ_ONLY,
1286 0 : name.ptr())) {
1287 0 : return JS_FALSE;
1288 : }
1289 : }
1290 9 : pn->setOp(op = JSOP_NAME);
1291 : }
1292 : }
1293 :
1294 18169851 : if (dn->isGlobal()) {
1295 182343 : if (op == JSOP_NAME) {
1296 : /*
1297 : * If the definition is a defined global, not potentially aliased
1298 : * by a local variable, and not mutating the variable, try and
1299 : * optimize to a fast, unguarded global access.
1300 : */
1301 126115 : if (!pn->pn_cookie.isFree()) {
1302 0 : pn->setOp(JSOP_GETGNAME);
1303 0 : pn->pn_dflags |= PND_BOUND;
1304 0 : return JS_TRUE;
1305 : }
1306 : }
1307 :
1308 : /*
1309 : * The locally stored cookie here should really come from |pn|, not
1310 : * |dn|. For example, we could have a SETGNAME op's lexdef be a
1311 : * GETGNAME op, and their cookies have very different meanings. As
1312 : * a workaround, just make the cookie free.
1313 : */
1314 182343 : cookie.makeFree();
1315 : }
1316 :
1317 18169851 : if (cookie.isFree()) {
1318 12070027 : StackFrame *caller = bce->parser->callerFrame;
1319 12070027 : if (caller) {
1320 25566 : JS_ASSERT(bce->compileAndGo());
1321 :
1322 : /*
1323 : * Don't generate upvars on the left side of a for loop. See
1324 : * bug 470758.
1325 : */
1326 25566 : if (bce->flags & TCF_IN_FOR_INIT)
1327 117 : return JS_TRUE;
1328 :
1329 25449 : JS_ASSERT(caller->isScriptFrame());
1330 :
1331 : /*
1332 : * If this is an eval in the global scope, then unbound variables
1333 : * must be globals, so try to use GNAME ops.
1334 : */
1335 25449 : if (caller->isGlobalFrame() && TryConvertToGname(bce, pn, &op)) {
1336 : jsatomid _;
1337 2011 : if (!bce->makeAtomIndex(atom, &_))
1338 0 : return JS_FALSE;
1339 :
1340 2011 : pn->setOp(op);
1341 2011 : pn->pn_dflags |= PND_BOUND;
1342 2011 : return JS_TRUE;
1343 : }
1344 :
1345 : /*
1346 : * Out of tricks, so we must rely on PICs to optimize named
1347 : * accesses from direct eval called from function code.
1348 : */
1349 23438 : return JS_TRUE;
1350 : }
1351 :
1352 : /* Optimize accesses to undeclared globals. */
1353 12044461 : if (!TryConvertToGname(bce, pn, &op))
1354 10199512 : return JS_TRUE;
1355 :
1356 : jsatomid _;
1357 1844949 : if (!bce->makeAtomIndex(atom, &_))
1358 0 : return JS_FALSE;
1359 :
1360 1844949 : pn->setOp(op);
1361 1844949 : pn->pn_dflags |= PND_BOUND;
1362 :
1363 1844949 : return JS_TRUE;
1364 : }
1365 :
1366 6099824 : uint16_t level = cookie.level();
1367 6099824 : JS_ASSERT(bce->staticLevel >= level);
1368 :
1369 6099824 : const unsigned skip = bce->staticLevel - level;
1370 6099824 : if (skip != 0) {
1371 637247 : JS_ASSERT(bce->inFunction());
1372 637247 : JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, bce->roLexdeps->lookup(atom));
1373 637247 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
1374 :
1375 : /*
1376 : * If op is a mutating opcode, this upvar's lookup skips too many levels,
1377 : * or the function is heavyweight, we fall back on JSOP_*NAME*.
1378 : */
1379 637247 : if (op != JSOP_NAME)
1380 37640 : return JS_TRUE;
1381 599607 : if (skip >= UpvarCookie::UPVAR_LEVEL_LIMIT)
1382 0 : return JS_TRUE;
1383 599607 : if (bce->flags & TCF_FUN_HEAVYWEIGHT)
1384 44725 : return JS_TRUE;
1385 :
1386 554882 : if (!bce->fun()->isFlatClosure())
1387 362051 : return JS_TRUE;
1388 :
1389 192831 : if (!bce->upvarIndices.ensureMap(cx))
1390 0 : return JS_FALSE;
1391 :
1392 192831 : AtomIndexAddPtr p = bce->upvarIndices->lookupForAdd(atom);
1393 : jsatomid index;
1394 192831 : if (p) {
1395 80127 : index = p.value();
1396 : } else {
1397 112704 : if (!bce->bindings.addUpvar(cx, atom))
1398 0 : return JS_FALSE;
1399 :
1400 112704 : index = bce->upvarIndices->count();
1401 112704 : if (!bce->upvarIndices->add(p, atom, index))
1402 0 : return JS_FALSE;
1403 :
1404 112704 : UpvarCookies &upvarMap = bce->upvarMap;
1405 : /* upvarMap should have the same number of UpvarCookies as there are lexdeps. */
1406 112704 : size_t lexdepCount = bce->roLexdeps->count();
1407 :
1408 112704 : JS_ASSERT_IF(!upvarMap.empty(), lexdepCount == upvarMap.length());
1409 112704 : if (upvarMap.empty()) {
1410 : /* Lazily initialize the upvar map with exactly the necessary capacity. */
1411 83617 : if (lexdepCount <= upvarMap.sMaxInlineStorage) {
1412 83522 : JS_ALWAYS_TRUE(upvarMap.growByUninitialized(lexdepCount));
1413 : } else {
1414 95 : void *buf = upvarMap.allocPolicy().malloc_(lexdepCount * sizeof(UpvarCookie));
1415 95 : if (!buf)
1416 0 : return JS_FALSE;
1417 95 : upvarMap.replaceRawBuffer(static_cast<UpvarCookie *>(buf), lexdepCount);
1418 : }
1419 289527 : for (size_t i = 0; i < lexdepCount; ++i)
1420 205910 : upvarMap[i] = UpvarCookie();
1421 : }
1422 :
1423 112704 : unsigned slot = cookie.slot();
1424 112704 : if (slot != UpvarCookie::CALLEE_SLOT && dn_kind != Definition::ARG) {
1425 37357 : TreeContext *tc = bce;
1426 37359 : do {
1427 37359 : tc = tc->parent;
1428 : } while (tc->staticLevel != level);
1429 37357 : if (tc->inFunction())
1430 37327 : slot += tc->fun()->nargs;
1431 : }
1432 :
1433 112704 : JS_ASSERT(index < upvarMap.length());
1434 112704 : upvarMap[index].set(skip, slot);
1435 : }
1436 :
1437 192831 : pn->setOp(JSOP_GETFCSLOT);
1438 192831 : JS_ASSERT((index & JS_BITMASK(16)) == index);
1439 192831 : pn->pn_cookie.set(0, index);
1440 192831 : pn->pn_dflags |= PND_BOUND;
1441 192831 : return JS_TRUE;
1442 : }
1443 :
1444 : /*
1445 : * We are compiling a function body and may be able to optimize name
1446 : * to stack slot. Look for an argument or variable in the function and
1447 : * rewrite pn_op and update pn accordingly.
1448 : */
1449 5462577 : switch (dn_kind) {
1450 : case Definition::UNKNOWN:
1451 0 : return JS_TRUE;
1452 :
1453 : case Definition::LET:
1454 958066 : switch (op) {
1455 841646 : case JSOP_NAME: op = JSOP_GETLOCAL; break;
1456 84626 : case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
1457 4561 : case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
1458 26027 : case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
1459 66 : case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
1460 1140 : case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
1461 0 : default: JS_NOT_REACHED("let");
1462 : }
1463 958066 : break;
1464 :
1465 : case Definition::ARG:
1466 2070654 : switch (op) {
1467 2020711 : case JSOP_NAME: op = JSOP_GETARG; break;
1468 48428 : case JSOP_SETNAME: op = JSOP_SETARG; break;
1469 1014 : case JSOP_INCNAME: op = JSOP_INCARG; break;
1470 163 : case JSOP_NAMEINC: op = JSOP_ARGINC; break;
1471 196 : case JSOP_DECNAME: op = JSOP_DECARG; break;
1472 142 : case JSOP_NAMEDEC: op = JSOP_ARGDEC; break;
1473 0 : default: JS_NOT_REACHED("arg");
1474 : }
1475 2070654 : JS_ASSERT(!pn->isConst());
1476 2070654 : break;
1477 :
1478 : case Definition::VAR:
1479 2351764 : if (dn->isOp(JSOP_CALLEE)) {
1480 1813 : JS_ASSERT(op != JSOP_CALLEE);
1481 1813 : JS_ASSERT((bce->fun()->flags & JSFUN_LAMBDA) && atom == bce->fun()->atom);
1482 :
1483 : /*
1484 : * Leave pn->isOp(JSOP_NAME) if bce->fun is heavyweight to
1485 : * address two cases: a new binding introduced by eval, and
1486 : * assignment to the name in strict mode.
1487 : *
1488 : * var fun = (function f(s) { eval(s); return f; });
1489 : * assertEq(fun("var f = 42"), 42);
1490 : *
1491 : * ECMAScript specifies that a function expression's name is bound
1492 : * in a lexical environment distinct from that used to bind its
1493 : * named parameters, the arguments object, and its variables. The
1494 : * new binding for "var f = 42" shadows the binding for the
1495 : * function itself, so the name of the function will not refer to
1496 : * the function.
1497 : *
1498 : * (function f() { "use strict"; f = 12; })();
1499 : *
1500 : * Outside strict mode, assignment to a function expression's name
1501 : * has no effect. But in strict mode, this attempt to mutate an
1502 : * immutable binding must throw a TypeError. We implement this by
1503 : * not optimizing such assignments and by marking such functions as
1504 : * heavyweight, ensuring that the function name is represented in
1505 : * the scope chain so that assignment will throw a TypeError.
1506 : */
1507 1813 : JS_ASSERT(op != JSOP_DELNAME);
1508 1813 : if (!(bce->flags & TCF_FUN_HEAVYWEIGHT)) {
1509 1774 : op = JSOP_CALLEE;
1510 1774 : pn->pn_dflags |= PND_CONST;
1511 : }
1512 :
1513 1813 : pn->setOp(op);
1514 1813 : pn->pn_dflags |= PND_BOUND;
1515 1813 : return JS_TRUE;
1516 : }
1517 : /* FALL THROUGH */
1518 :
1519 : default:
1520 0 : JS_ASSERT_IF(dn_kind != Definition::FUNCTION,
1521 : dn_kind == Definition::VAR ||
1522 2432044 : dn_kind == Definition::CONST);
1523 2432044 : switch (op) {
1524 1939216 : case JSOP_NAME: op = JSOP_GETLOCAL; break;
1525 455457 : case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
1526 0 : case JSOP_SETCONST: op = JSOP_SETLOCAL; break;
1527 7991 : case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
1528 28611 : case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
1529 314 : case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
1530 455 : case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
1531 0 : default: JS_NOT_REACHED("local");
1532 : }
1533 2432044 : JS_ASSERT_IF(dn_kind == Definition::CONST, pn->pn_dflags & PND_CONST);
1534 2432044 : break;
1535 : }
1536 :
1537 5460764 : JS_ASSERT(!pn->isOp(op));
1538 5460764 : pn->setOp(op);
1539 5460764 : pn->pn_cookie.set(0, cookie.slot());
1540 5460764 : pn->pn_dflags |= PND_BOUND;
1541 5460764 : return JS_TRUE;
1542 : }
1543 :
1544 : bool
1545 24079 : BytecodeEmitter::addGlobalUse(JSAtom *atom, uint32_t slot, UpvarCookie *cookie)
1546 : {
1547 24079 : if (!globalMap.ensureMap(context()))
1548 0 : return false;
1549 :
1550 24079 : AtomIndexAddPtr p = globalMap->lookupForAdd(atom);
1551 24079 : if (p) {
1552 351 : jsatomid index = p.value();
1553 351 : cookie->set(0, index);
1554 351 : return true;
1555 : }
1556 :
1557 : /* Don't bother encoding indexes >= uint16_t */
1558 23728 : if (globalUses.length() >= UINT16_LIMIT) {
1559 0 : cookie->makeFree();
1560 0 : return true;
1561 : }
1562 :
1563 : /* Find or add an existing atom table entry. */
1564 : jsatomid allAtomIndex;
1565 23728 : if (!makeAtomIndex(atom, &allAtomIndex))
1566 0 : return false;
1567 :
1568 23728 : jsatomid globalUseIndex = globalUses.length();
1569 23728 : cookie->set(0, globalUseIndex);
1570 :
1571 23728 : GlobalSlotArray::Entry entry = { allAtomIndex, slot };
1572 23728 : if (!globalUses.append(entry))
1573 0 : return false;
1574 :
1575 23728 : return globalMap->add(p, atom, globalUseIndex);
1576 : }
1577 :
1578 : /*
1579 : * If pn contains a useful expression, return true with *answer set to true.
1580 : * If pn contains a useless expression, return true with *answer set to false.
1581 : * Return false on error.
1582 : *
1583 : * The caller should initialize *answer to false and invoke this function on
1584 : * an expression statement or similar subtree to decide whether the tree could
1585 : * produce code that has any side effects. For an expression statement, we
1586 : * define useless code as code with no side effects, because the main effect,
1587 : * the value left on the stack after the code executes, will be discarded by a
1588 : * pop bytecode.
1589 : */
1590 : static JSBool
1591 6587134 : CheckSideEffects(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool *answer)
1592 : {
1593 : JSBool ok;
1594 : ParseNode *pn2;
1595 :
1596 6587134 : ok = JS_TRUE;
1597 6587134 : if (!pn || *answer)
1598 154546 : return ok;
1599 :
1600 6432588 : switch (pn->getArity()) {
1601 : case PN_FUNC:
1602 : /*
1603 : * A named function, contrary to ES3, is no longer useful, because we
1604 : * bind its name lexically (using JSOP_CALLEE) instead of creating an
1605 : * Object instance and binding a readonly, permanent property in it
1606 : * (the object and binding can be detected and hijacked or captured).
1607 : * This is a bug fix to ES3; it is fixed in ES3.1 drafts.
1608 : */
1609 37771 : *answer = JS_FALSE;
1610 37771 : break;
1611 :
1612 : case PN_LIST:
1613 5600967 : if (pn->isOp(JSOP_NOP) || pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) ||
1614 3732418 : pn->isOp(JSOP_STRICTEQ) || pn->isOp(JSOP_STRICTNE)) {
1615 : /*
1616 : * Non-operators along with ||, &&, ===, and !== never invoke
1617 : * toString or valueOf.
1618 : */
1619 5614 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
1620 3274 : ok &= CheckSideEffects(cx, bce, pn2, answer);
1621 : } else {
1622 : /*
1623 : * All invocation operations (construct: PNK_NEW, call: PNK_LP)
1624 : * are presumed to be useful, because they may have side effects
1625 : * even if their main effect (their return value) is discarded.
1626 : *
1627 : * PNK_LB binary trees of 3 or more nodes are flattened into lists
1628 : * to avoid too much recursion. All such lists must be presumed
1629 : * to be useful because each index operation could invoke a getter
1630 : * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case,
1631 : * does not apply here: arguments[i][j] might invoke a getter).
1632 : *
1633 : * Likewise, array and object initialisers may call prototype
1634 : * setters (the __defineSetter__ built-in, and writable __proto__
1635 : * on Array.prototype create this hazard). Initialiser list nodes
1636 : * have JSOP_NEWINIT in their pn_op.
1637 : */
1638 1866209 : *answer = JS_TRUE;
1639 : }
1640 1868549 : break;
1641 :
1642 : case PN_TERNARY:
1643 6316 : ok = CheckSideEffects(cx, bce, pn->pn_kid1, answer) &&
1644 6316 : CheckSideEffects(cx, bce, pn->pn_kid2, answer) &&
1645 12632 : CheckSideEffects(cx, bce, pn->pn_kid3, answer);
1646 6316 : break;
1647 :
1648 : case PN_BINARY:
1649 2435656 : if (pn->isAssignment()) {
1650 : /*
1651 : * Assignment is presumed to be useful, even if the next operation
1652 : * is another assignment overwriting this one's ostensible effect,
1653 : * because the left operand may be a property with a setter that
1654 : * has side effects.
1655 : *
1656 : * The only exception is assignment of a useless value to a const
1657 : * declared in the function currently being compiled.
1658 : */
1659 2377139 : pn2 = pn->pn_left;
1660 2377139 : if (!pn2->isKind(PNK_NAME)) {
1661 1745557 : *answer = JS_TRUE;
1662 : } else {
1663 631582 : if (!BindNameToSlot(cx, bce, pn2))
1664 0 : return JS_FALSE;
1665 631582 : if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
1666 0 : return JS_FALSE;
1667 631582 : if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
1668 375508 : *answer = JS_TRUE;
1669 : }
1670 : } else {
1671 109036 : if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) ||
1672 50519 : pn->isOp(JSOP_STRICTNE)) {
1673 : /*
1674 : * ||, &&, ===, and !== do not convert their operands via
1675 : * toString or valueOf method calls.
1676 : */
1677 8025 : ok = CheckSideEffects(cx, bce, pn->pn_left, answer) &&
1678 8025 : CheckSideEffects(cx, bce, pn->pn_right, answer);
1679 : } else {
1680 : /*
1681 : * We can't easily prove that neither operand ever denotes an
1682 : * object with a toString or valueOf method.
1683 : */
1684 50492 : *answer = JS_TRUE;
1685 : }
1686 : }
1687 2435656 : break;
1688 :
1689 : case PN_UNARY:
1690 1667139 : switch (pn->getKind()) {
1691 : case PNK_DELETE:
1692 37421 : pn2 = pn->pn_kid;
1693 37421 : switch (pn2->getKind()) {
1694 : case PNK_NAME:
1695 464 : if (!BindNameToSlot(cx, bce, pn2))
1696 0 : return JS_FALSE;
1697 464 : if (pn2->isConst()) {
1698 0 : *answer = JS_FALSE;
1699 0 : break;
1700 : }
1701 : /* FALL THROUGH */
1702 : case PNK_DOT:
1703 : #if JS_HAS_XML_SUPPORT
1704 : case PNK_DBLDOT:
1705 23343 : JS_ASSERT_IF(pn2->getKind() == PNK_DBLDOT, !bce->inStrictMode());
1706 : /* FALL THROUGH */
1707 :
1708 : #endif
1709 : case PNK_LP:
1710 : case PNK_LB:
1711 : /* All these delete addressing modes have effects too. */
1712 37421 : *answer = JS_TRUE;
1713 37421 : break;
1714 : default:
1715 0 : ok = CheckSideEffects(cx, bce, pn2, answer);
1716 0 : break;
1717 : }
1718 37421 : break;
1719 :
1720 : case PNK_TYPEOF:
1721 : case PNK_VOID:
1722 : case PNK_NOT:
1723 : case PNK_BITNOT:
1724 3450 : if (pn->isOp(JSOP_NOT)) {
1725 : /* ! does not convert its operand via toString or valueOf. */
1726 3405 : ok = CheckSideEffects(cx, bce, pn->pn_kid, answer);
1727 3405 : break;
1728 : }
1729 : /* FALL THROUGH */
1730 :
1731 : default:
1732 : /*
1733 : * All of PNK_INC, PNK_DEC, PNK_THROW, and PNK_YIELD have direct
1734 : * effects. Of the remaining unary-arity node types, we can't
1735 : * easily prove that the operand never denotes an object with a
1736 : * toString or valueOf method.
1737 : */
1738 1626313 : *answer = JS_TRUE;
1739 1626313 : break;
1740 : }
1741 1667139 : break;
1742 :
1743 : case PN_NAME:
1744 : /*
1745 : * Take care to avoid trying to bind a label name (labels, both for
1746 : * statements and property values in object initialisers, have pn_op
1747 : * defaulted to JSOP_NOP).
1748 : */
1749 139522 : if (pn->isKind(PNK_NAME) && !pn->isOp(JSOP_NOP)) {
1750 76275 : if (!BindNameToSlot(cx, bce, pn))
1751 0 : return JS_FALSE;
1752 152433 : if (!pn->isOp(JSOP_ARGUMENTS) && !pn->isOp(JSOP_CALLEE) &&
1753 76158 : pn->pn_cookie.isFree()) {
1754 : /*
1755 : * Not an argument or local variable use, and not a use of a
1756 : * unshadowed named function expression's given name, so this
1757 : * expression could invoke a getter that has side effects.
1758 : */
1759 6144 : *answer = JS_TRUE;
1760 : }
1761 : }
1762 139522 : pn2 = pn->maybeExpr();
1763 139522 : if (pn->isKind(PNK_DOT)) {
1764 63247 : if (pn2->isKind(PNK_NAME) && !BindNameToSlot(cx, bce, pn2))
1765 0 : return JS_FALSE;
1766 126485 : if (!(pn2->isOp(JSOP_ARGUMENTS) &&
1767 9 : pn->pn_atom == cx->runtime->atomState.lengthAtom)) {
1768 : /*
1769 : * Any dotted property reference could call a getter, except
1770 : * for arguments.length where arguments is unambiguous.
1771 : */
1772 63238 : *answer = JS_TRUE;
1773 : }
1774 : }
1775 139522 : ok = CheckSideEffects(cx, bce, pn2, answer);
1776 139522 : break;
1777 :
1778 : case PN_NAMESET:
1779 0 : ok = CheckSideEffects(cx, bce, pn->pn_tree, answer);
1780 0 : break;
1781 :
1782 : case PN_NULLARY:
1783 277635 : if (pn->isKind(PNK_DEBUGGER))
1784 0 : *answer = JS_TRUE;
1785 277635 : break;
1786 : }
1787 6432588 : return ok;
1788 : }
1789 :
1790 : bool
1791 674054 : BytecodeEmitter::needsImplicitThis()
1792 : {
1793 674054 : if (!compileAndGo())
1794 668631 : return true;
1795 5423 : if (!inFunction()) {
1796 1630 : JSObject *scope = scopeChain();
1797 6016 : while (scope) {
1798 2756 : if (scope->isWith())
1799 0 : return true;
1800 2756 : scope = scope->enclosingScope();
1801 : }
1802 : }
1803 13750 : for (const FunctionBox *funbox = this->funbox; funbox; funbox = funbox->parent) {
1804 8354 : if (funbox->tcflags & TCF_IN_WITH)
1805 27 : return true;
1806 : }
1807 14328 : for (StmtInfo *stmt = topStmt; stmt; stmt = stmt->down) {
1808 9112 : if (stmt->type == STMT_WITH)
1809 180 : return true;
1810 : }
1811 5216 : return false;
1812 : }
1813 :
1814 : static JSBool
1815 13083746 : EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContext)
1816 : {
1817 : JSOp op;
1818 :
1819 13083746 : if (!BindNameToSlot(cx, bce, pn))
1820 0 : return JS_FALSE;
1821 13083746 : op = pn->getOp();
1822 :
1823 13083746 : if (callContext) {
1824 860698 : switch (op) {
1825 : case JSOP_NAME:
1826 674054 : op = JSOP_CALLNAME;
1827 674054 : break;
1828 : case JSOP_GETGNAME:
1829 102629 : op = JSOP_CALLGNAME;
1830 102629 : break;
1831 : case JSOP_GETARG:
1832 17061 : op = JSOP_CALLARG;
1833 17061 : break;
1834 : case JSOP_GETLOCAL:
1835 44945 : op = JSOP_CALLLOCAL;
1836 44945 : break;
1837 : case JSOP_GETFCSLOT:
1838 21558 : op = JSOP_CALLFCSLOT;
1839 21558 : break;
1840 : default:
1841 451 : JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE);
1842 451 : break;
1843 : }
1844 : }
1845 :
1846 13083746 : if (op == JSOP_ARGUMENTS) {
1847 13766 : if (!EmitArguments(cx, bce))
1848 0 : return JS_FALSE;
1849 13069980 : } else if (op == JSOP_CALLEE) {
1850 1774 : if (Emit1(cx, bce, op) < 0)
1851 0 : return JS_FALSE;
1852 : } else {
1853 13068206 : if (!pn->pn_cookie.isFree()) {
1854 4994802 : JS_ASSERT(JOF_OPTYPE(op) != JOF_ATOM);
1855 4994802 : EMIT_UINT16_IMM_OP(op, pn->pn_cookie.slot());
1856 : } else {
1857 8073404 : if (!EmitAtomOp(cx, pn, op, bce))
1858 0 : return JS_FALSE;
1859 : }
1860 : }
1861 :
1862 : /* Need to provide |this| value for call */
1863 13083746 : if (callContext) {
1864 860698 : if (op == JSOP_CALLNAME && bce->needsImplicitThis()) {
1865 668838 : if (!EmitAtomOp(cx, pn, JSOP_IMPLICITTHIS, bce))
1866 0 : return false;
1867 : } else {
1868 191860 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
1869 0 : return false;
1870 : }
1871 : }
1872 :
1873 13083746 : return JS_TRUE;
1874 : }
1875 :
1876 : #if JS_HAS_XML_SUPPORT
1877 : static bool
1878 63 : EmitXMLName(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
1879 : {
1880 63 : JS_ASSERT(!bce->inStrictMode());
1881 63 : JS_ASSERT(pn->isKind(PNK_XMLUNARY));
1882 63 : JS_ASSERT(pn->isOp(JSOP_XMLNAME));
1883 63 : JS_ASSERT(op == JSOP_XMLNAME || op == JSOP_CALLXMLNAME);
1884 :
1885 63 : ParseNode *pn2 = pn->pn_kid;
1886 63 : unsigned oldflags = bce->flags;
1887 63 : bce->flags &= ~TCF_IN_FOR_INIT;
1888 63 : if (!EmitTree(cx, bce, pn2))
1889 0 : return false;
1890 63 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
1891 63 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
1892 0 : return false;
1893 :
1894 63 : if (Emit1(cx, bce, op) < 0)
1895 0 : return false;
1896 :
1897 63 : return true;
1898 : }
1899 : #endif
1900 :
1901 : static inline bool
1902 5242502 : EmitElemOpBase(JSContext *cx, BytecodeEmitter *bce, JSOp op)
1903 : {
1904 5242502 : if (Emit1(cx, bce, op) < 0)
1905 0 : return false;
1906 5242502 : CheckTypeSet(cx, bce, op);
1907 5242502 : if (op == JSOP_CALLELEM)
1908 4731 : return Emit1(cx, bce, JSOP_SWAP) >= 0;
1909 5237771 : return true;
1910 : }
1911 :
1912 : static bool
1913 388 : EmitSpecialPropOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
1914 : {
1915 : /*
1916 : * Special case for obj.__proto__ to deoptimize away from fast paths in the
1917 : * interpreter and trace recorder, which skip dense array instances by
1918 : * going up to Array.prototype before looking up the property name.
1919 : */
1920 388 : if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
1921 0 : return false;
1922 :
1923 : jsatomid index;
1924 388 : if (!bce->makeAtomIndex(pn->pn_atom, &index))
1925 0 : return false;
1926 388 : if (!EmitIndex32(cx, JSOP_QNAMEPART, index, bce))
1927 0 : return false;
1928 :
1929 388 : return EmitElemOpBase(cx, bce, op);
1930 : }
1931 :
1932 : static bool
1933 8183264 : EmitPropOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce,
1934 : JSBool callContext)
1935 : {
1936 : ParseNode *pn2, *pndot, *pnup, *pndown;
1937 : ptrdiff_t top;
1938 :
1939 8183264 : JS_ASSERT(pn->isArity(PN_NAME));
1940 8183264 : pn2 = pn->maybeExpr();
1941 :
1942 : /* Special case deoptimization for __proto__. */
1943 8183264 : if ((op == JSOP_GETPROP || op == JSOP_CALLPROP) &&
1944 : pn->pn_atom == cx->runtime->atomState.protoAtom) {
1945 327 : if (pn2 && !EmitTree(cx, bce, pn2))
1946 0 : return false;
1947 327 : return EmitSpecialPropOp(cx, pn, callContext ? JSOP_CALLELEM : JSOP_GETELEM, bce);
1948 : }
1949 :
1950 8182937 : if (callContext) {
1951 2297637 : JS_ASSERT(pn->isKind(PNK_DOT));
1952 2297637 : JS_ASSERT(op == JSOP_GETPROP);
1953 2297637 : op = JSOP_CALLPROP;
1954 5885300 : } else if (op == JSOP_GETPROP && pn->isKind(PNK_DOT)) {
1955 5857511 : if (pn2->isKind(PNK_NAME)) {
1956 4856575 : if (!BindNameToSlot(cx, bce, pn2))
1957 0 : return false;
1958 : }
1959 : }
1960 :
1961 : /*
1962 : * If the object operand is also a dotted property reference, reverse the
1963 : * list linked via pn_expr temporarily so we can iterate over it from the
1964 : * bottom up (reversing again as we go), to avoid excessive recursion.
1965 : */
1966 8182937 : if (pn2->isKind(PNK_DOT)) {
1967 1036214 : pndot = pn2;
1968 1036214 : pnup = NULL;
1969 1036214 : top = bce->offset();
1970 54144 : for (;;) {
1971 : /* Reverse pndot->pn_expr to point up, not down. */
1972 1090358 : pndot->pn_offset = top;
1973 1090358 : JS_ASSERT(!pndot->isUsed());
1974 1090358 : pndown = pndot->pn_expr;
1975 1090358 : pndot->pn_expr = pnup;
1976 1090358 : if (!pndown->isKind(PNK_DOT))
1977 : break;
1978 54144 : pnup = pndot;
1979 54144 : pndot = pndown;
1980 : }
1981 :
1982 : /* pndown is a primary expression, not a dotted property reference. */
1983 1036214 : if (!EmitTree(cx, bce, pndown))
1984 0 : return false;
1985 :
1986 1090358 : do {
1987 : /* Walk back up the list, emitting annotated name ops. */
1988 1090358 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pndown->pn_offset) < 0)
1989 0 : return false;
1990 :
1991 : /* Special case deoptimization on __proto__, as above. */
1992 1090358 : if (pndot->isArity(PN_NAME) && pndot->pn_atom == cx->runtime->atomState.protoAtom) {
1993 61 : if (!EmitSpecialPropOp(cx, pndot, JSOP_GETELEM, bce))
1994 0 : return false;
1995 1090297 : } else if (!EmitAtomOp(cx, pndot, pndot->getOp(), bce)) {
1996 0 : return false;
1997 : }
1998 :
1999 : /* Reverse the pn_expr link again. */
2000 1090358 : pnup = pndot->pn_expr;
2001 1090358 : pndot->pn_expr = pndown;
2002 1090358 : pndown = pndot;
2003 : } while ((pndot = pnup) != NULL);
2004 : } else {
2005 7146723 : if (!EmitTree(cx, bce, pn2))
2006 0 : return false;
2007 : }
2008 :
2009 8182937 : if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_DUP) < 0)
2010 0 : return false;
2011 :
2012 8182937 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
2013 0 : return false;
2014 :
2015 8182937 : if (!EmitAtomOp(cx, pn, op, bce))
2016 0 : return false;
2017 :
2018 8182937 : if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_SWAP) < 0)
2019 0 : return false;
2020 :
2021 8182937 : return true;
2022 : }
2023 :
2024 : static bool
2025 4874 : EmitPropIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2026 : {
2027 4874 : if (!EmitPropOp(cx, pn, op, bce, false))
2028 0 : return false;
2029 :
2030 : /*
2031 : * The stack is the same depth before/after INCPROP, so no balancing to do
2032 : * before the decomposed version.
2033 : */
2034 4874 : int start = bce->offset();
2035 :
2036 4874 : const JSCodeSpec *cs = &js_CodeSpec[op];
2037 4874 : JS_ASSERT(cs->format & JOF_PROP);
2038 4874 : JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
2039 :
2040 4874 : bool post = (cs->format & JOF_POST);
2041 4874 : JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
2042 :
2043 : // OBJ
2044 4874 : if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ
2045 0 : return false;
2046 4874 : if (!EmitAtomOp(cx, pn, JSOP_GETPROP, bce)) // OBJ V
2047 0 : return false;
2048 4874 : if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ N
2049 0 : return false;
2050 4874 : if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ N? N
2051 0 : return false;
2052 4874 : if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ N? N 1
2053 0 : return false;
2054 4874 : if (Emit1(cx, bce, binop) < 0) // OBJ N? N+1
2055 0 : return false;
2056 :
2057 4874 : if (post) {
2058 4386 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
2059 0 : return false;
2060 4386 : if (Emit1(cx, bce, JSOP_SWAP) < 0) // N? OBJ N+1
2061 0 : return false;
2062 : }
2063 :
2064 4874 : if (!EmitAtomOp(cx, pn, JSOP_SETPROP, bce)) // N? N+1
2065 0 : return false;
2066 4874 : if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
2067 0 : return false;
2068 :
2069 4874 : UpdateDecomposeLength(bce, start);
2070 :
2071 4874 : return true;
2072 : }
2073 :
2074 : static bool
2075 22755 : EmitNameIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2076 : {
2077 : /* Emit the composite op, including the slack byte at the end. */
2078 22755 : if (!EmitAtomIncDec(cx, pn->pn_atom, op, bce))
2079 0 : return false;
2080 :
2081 : /* Remove the result to restore the stack depth before the INCNAME. */
2082 22755 : bce->stackDepth--;
2083 :
2084 22755 : int start = bce->offset();
2085 :
2086 22755 : const JSCodeSpec *cs = &js_CodeSpec[op];
2087 22755 : JS_ASSERT((cs->format & JOF_NAME) || (cs->format & JOF_GNAME));
2088 22755 : JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
2089 :
2090 22755 : bool global = (cs->format & JOF_GNAME);
2091 22755 : bool post = (cs->format & JOF_POST);
2092 22755 : JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
2093 :
2094 22755 : if (!EmitAtomOp(cx, pn, global ? JSOP_BINDGNAME : JSOP_BINDNAME, bce)) // OBJ
2095 0 : return false;
2096 22755 : if (!EmitAtomOp(cx, pn, global ? JSOP_GETGNAME : JSOP_NAME, bce)) // OBJ V
2097 0 : return false;
2098 22755 : if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ N
2099 0 : return false;
2100 22755 : if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ N? N
2101 0 : return false;
2102 22755 : if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ N? N 1
2103 0 : return false;
2104 22755 : if (Emit1(cx, bce, binop) < 0) // OBJ N? N+1
2105 0 : return false;
2106 :
2107 22755 : if (post) {
2108 9165 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
2109 0 : return false;
2110 9165 : if (Emit1(cx, bce, JSOP_SWAP) < 0) // N? OBJ N+1
2111 0 : return false;
2112 : }
2113 :
2114 22755 : if (!EmitAtomOp(cx, pn, global ? JSOP_SETGNAME : JSOP_SETNAME, bce)) // N? N+1
2115 0 : return false;
2116 22755 : if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
2117 0 : return false;
2118 :
2119 22755 : UpdateDecomposeLength(bce, start);
2120 :
2121 22755 : return true;
2122 : }
2123 :
2124 : static JSBool
2125 1931543 : EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2126 : {
2127 : ParseNode *left, *right;
2128 :
2129 1931543 : ptrdiff_t top = bce->offset();
2130 :
2131 1931543 : if (pn->isArity(PN_NAME)) {
2132 : /*
2133 : * Set left and right so pn appears to be a PNK_LB node, instead
2134 : * of a PNK_DOT node. See the PNK_FOR/IN case in EmitTree, and
2135 : * EmitDestructuringOps nearer below. In the destructuring case,
2136 : * the base expression (pn_expr) of the name may be null, which
2137 : * means we have to emit a JSOP_BINDNAME.
2138 : */
2139 1205 : left = pn->maybeExpr();
2140 1205 : if (!left) {
2141 1101 : left = NullaryNode::create(PNK_STRING, bce);
2142 1101 : if (!left)
2143 0 : return false;
2144 1101 : left->setOp(JSOP_BINDNAME);
2145 1101 : left->pn_pos = pn->pn_pos;
2146 1101 : left->pn_atom = pn->pn_atom;
2147 : }
2148 1205 : right = NullaryNode::create(PNK_STRING, bce);
2149 1205 : if (!right)
2150 0 : return false;
2151 1205 : right->setOp(IsIdentifier(pn->pn_atom) ? JSOP_QNAMEPART : JSOP_STRING);
2152 1205 : right->pn_pos = pn->pn_pos;
2153 1205 : right->pn_atom = pn->pn_atom;
2154 : } else {
2155 1930338 : JS_ASSERT(pn->isArity(PN_BINARY));
2156 1930338 : left = pn->pn_left;
2157 1930338 : right = pn->pn_right;
2158 : }
2159 :
2160 1931543 : if (op == JSOP_GETELEM && left->isKind(PNK_NAME) && right->isKind(PNK_NUMBER)) {
2161 36733 : if (!BindNameToSlot(cx, bce, left))
2162 0 : return false;
2163 : }
2164 :
2165 1931543 : if (!EmitTree(cx, bce, left))
2166 0 : return false;
2167 :
2168 1931543 : if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
2169 0 : return false;
2170 :
2171 : /* The right side of the descendant operator is implicitly quoted. */
2172 18 : JS_ASSERT(op != JSOP_DESCENDANTS || !right->isKind(PNK_STRING) ||
2173 1931561 : right->isOp(JSOP_QNAMEPART));
2174 1931543 : if (!EmitTree(cx, bce, right))
2175 0 : return false;
2176 1931543 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - top) < 0)
2177 0 : return false;
2178 1931543 : return EmitElemOpBase(cx, bce, op);
2179 : }
2180 :
2181 : static bool
2182 1638860 : EmitElemIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2183 : {
2184 1638860 : if (pn) {
2185 1638851 : if (!EmitElemOp(cx, pn, op, bce))
2186 0 : return false;
2187 : } else {
2188 9 : if (!EmitElemOpBase(cx, bce, op))
2189 0 : return false;
2190 : }
2191 1638860 : if (Emit1(cx, bce, JSOP_NOP) < 0)
2192 0 : return false;
2193 :
2194 : /* INCELEM pops two values and pushes one, so restore the initial depth. */
2195 1638860 : bce->stackDepth++;
2196 :
2197 1638860 : int start = bce->offset();
2198 :
2199 1638860 : const JSCodeSpec *cs = &js_CodeSpec[op];
2200 1638860 : JS_ASSERT(cs->format & JOF_ELEM);
2201 1638860 : JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
2202 :
2203 1638860 : bool post = (cs->format & JOF_POST);
2204 1638860 : JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
2205 :
2206 : /*
2207 : * We need to convert the key to an object id first, so that we do not do
2208 : * it inside both the GETELEM and the SETELEM.
2209 : */
2210 : // OBJ KEY*
2211 1638860 : if (Emit1(cx, bce, JSOP_TOID) < 0) // OBJ KEY
2212 0 : return false;
2213 1638860 : if (Emit1(cx, bce, JSOP_DUP2) < 0) // OBJ KEY OBJ KEY
2214 0 : return false;
2215 1638860 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM)) // OBJ KEY V
2216 0 : return false;
2217 1638860 : if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ KEY N
2218 0 : return false;
2219 1638860 : if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ KEY N? N
2220 0 : return false;
2221 1638860 : if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ KEY N? N 1
2222 0 : return false;
2223 1638860 : if (Emit1(cx, bce, binop) < 0) // OBJ KEY N? N+1
2224 0 : return false;
2225 :
2226 1638860 : if (post) {
2227 1638743 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // KEY N N+1 OBJ
2228 0 : return false;
2229 1638743 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // N N+1 OBJ KEY
2230 0 : return false;
2231 1638743 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N OBJ KEY N+1
2232 0 : return false;
2233 : }
2234 :
2235 1638860 : if (!EmitElemOpBase(cx, bce, JSOP_SETELEM)) // N? N+1
2236 0 : return false;
2237 1638860 : if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
2238 0 : return false;
2239 :
2240 1638860 : UpdateDecomposeLength(bce, start);
2241 :
2242 1638860 : return true;
2243 : }
2244 :
2245 : static JSBool
2246 11210671 : EmitNumberOp(JSContext *cx, double dval, BytecodeEmitter *bce)
2247 : {
2248 : int32_t ival;
2249 : uint32_t u;
2250 : ptrdiff_t off;
2251 : jsbytecode *pc;
2252 :
2253 11210671 : if (JSDOUBLE_IS_INT32(dval, &ival)) {
2254 11199202 : if (ival == 0)
2255 2131128 : return Emit1(cx, bce, JSOP_ZERO) >= 0;
2256 9068074 : if (ival == 1)
2257 298640 : return Emit1(cx, bce, JSOP_ONE) >= 0;
2258 8769434 : if ((int)(int8_t)ival == ival)
2259 1468828 : return Emit2(cx, bce, JSOP_INT8, (jsbytecode)(int8_t)ival) >= 0;
2260 :
2261 7300606 : u = (uint32_t)ival;
2262 7300606 : if (u < JS_BIT(16)) {
2263 7291105 : EMIT_UINT16_IMM_OP(JSOP_UINT16, u);
2264 9501 : } else if (u < JS_BIT(24)) {
2265 3392 : off = EmitN(cx, bce, JSOP_UINT24, 3);
2266 3392 : if (off < 0)
2267 0 : return JS_FALSE;
2268 3392 : pc = bce->code(off);
2269 3392 : SET_UINT24(pc, u);
2270 : } else {
2271 6109 : off = EmitN(cx, bce, JSOP_INT32, 4);
2272 6109 : if (off < 0)
2273 0 : return JS_FALSE;
2274 6109 : pc = bce->code(off);
2275 6109 : SET_INT32(pc, ival);
2276 : }
2277 7300606 : return JS_TRUE;
2278 : }
2279 :
2280 11469 : if (!bce->constList.append(DoubleValue(dval)))
2281 0 : return JS_FALSE;
2282 :
2283 11469 : return EmitIndex32(cx, JSOP_DOUBLE, bce->constList.length() - 1, bce);
2284 : }
2285 :
2286 : /*
2287 : * To avoid bloating all parse nodes for the special case of switch, values are
2288 : * allocated in the temp pool and pointed to by the parse node. These values
2289 : * are not currently recycled (like parse nodes) and the temp pool is only
2290 : * flushed at the end of compiling a script, so these values are technically
2291 : * leaked. This would only be a problem for scripts containing a large number
2292 : * of large switches, which seems unlikely.
2293 : */
2294 : static Value *
2295 48116 : AllocateSwitchConstant(JSContext *cx)
2296 : {
2297 48116 : return cx->tempLifoAlloc().new_<Value>();
2298 : }
2299 :
2300 : static inline void
2301 1287127 : SetJumpOffsetAt(BytecodeEmitter *bce, ptrdiff_t off)
2302 : {
2303 1287127 : SET_JUMP_OFFSET(bce->code(off), bce->offset() - off);
2304 1287127 : }
2305 :
2306 : /*
2307 : * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127.
2308 : * LLVM is deciding to inline this function which uses a lot of stack space
2309 : * into EmitTree which is recursive and uses relatively little stack space.
2310 : */
2311 : MOZ_NEVER_INLINE static JSBool
2312 17231 : EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
2313 : {
2314 : JSOp switchOp;
2315 : JSBool ok, hasDefault, constPropagated;
2316 : ptrdiff_t top, off, defaultOffset;
2317 : ParseNode *pn2, *pn3, *pn4;
2318 : uint32_t caseCount, tableLength;
2319 : ParseNode **table;
2320 : int32_t i, low, high;
2321 : int noteIndex;
2322 : size_t switchSize, tableSize;
2323 : jsbytecode *pc, *savepc;
2324 : StmtInfo stmtInfo;
2325 :
2326 : /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
2327 17231 : switchOp = JSOP_TABLESWITCH;
2328 17231 : ok = JS_TRUE;
2329 17231 : hasDefault = constPropagated = JS_FALSE;
2330 17231 : defaultOffset = -1;
2331 :
2332 17231 : pn2 = pn->pn_right;
2333 : #if JS_HAS_BLOCK_SCOPE
2334 : /*
2335 : * If there are hoisted let declarations, their stack slots go under the
2336 : * discriminant's value so push their slots now and enter the block later.
2337 : */
2338 17231 : uint32_t blockObjCount = 0;
2339 17231 : if (pn2->isKind(PNK_LEXICALSCOPE)) {
2340 3521 : blockObjCount = pn2->pn_objbox->object->asStaticBlock().slotCount();
2341 13957 : for (uint32_t i = 0; i < blockObjCount; ++i) {
2342 10436 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
2343 0 : return JS_FALSE;
2344 : }
2345 : }
2346 : #endif
2347 :
2348 : /* Push the discriminant. */
2349 17231 : if (!EmitTree(cx, bce, pn->pn_left))
2350 0 : return JS_FALSE;
2351 :
2352 : #if JS_HAS_BLOCK_SCOPE
2353 17231 : if (pn2->isKind(PNK_LEXICALSCOPE)) {
2354 3521 : PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object->asStaticBlock(), -1);
2355 3521 : stmtInfo.type = STMT_SWITCH;
2356 3521 : if (!EmitEnterBlock(cx, bce, pn2, JSOP_ENTERLET1))
2357 0 : return JS_FALSE;
2358 : }
2359 : #endif
2360 :
2361 : /* Switch bytecodes run from here till end of final case. */
2362 17231 : top = bce->offset();
2363 : #if !JS_HAS_BLOCK_SCOPE
2364 : PushStatement(bce, &stmtInfo, STMT_SWITCH, top);
2365 : #else
2366 17231 : if (pn2->isKind(PNK_STATEMENTLIST)) {
2367 13710 : PushStatement(bce, &stmtInfo, STMT_SWITCH, top);
2368 : } else {
2369 : /*
2370 : * Set the statement info record's idea of top. Reset top too, since
2371 : * repushBlock emits code.
2372 : */
2373 3521 : stmtInfo.update = top = bce->offset();
2374 :
2375 : /* Advance pn2 to refer to the switch case list. */
2376 3521 : pn2 = pn2->expr();
2377 : }
2378 : #endif
2379 :
2380 17231 : caseCount = pn2->pn_count;
2381 17231 : tableLength = 0;
2382 17231 : table = NULL;
2383 :
2384 17641 : if (caseCount == 0 ||
2385 : (caseCount == 1 &&
2386 410 : (hasDefault = (pn2->pn_head->isKind(PNK_DEFAULT))))) {
2387 81 : caseCount = 0;
2388 81 : low = 0;
2389 81 : high = -1;
2390 : } else {
2391 : #define INTMAP_LENGTH 256
2392 : jsbitmap intmap_space[INTMAP_LENGTH];
2393 17150 : jsbitmap *intmap = NULL;
2394 17150 : int32_t intmap_bitlen = 0;
2395 :
2396 17150 : low = JSVAL_INT_MAX;
2397 17150 : high = JSVAL_INT_MIN;
2398 :
2399 101667 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2400 84517 : if (pn3->isKind(PNK_DEFAULT)) {
2401 9477 : hasDefault = JS_TRUE;
2402 9477 : caseCount--; /* one of the "cases" was the default */
2403 9477 : continue;
2404 : }
2405 :
2406 75040 : JS_ASSERT(pn3->isKind(PNK_CASE));
2407 75040 : if (switchOp == JSOP_CONDSWITCH)
2408 19911 : continue;
2409 :
2410 55129 : pn4 = pn3->pn_left;
2411 110258 : while (pn4->isKind(PNK_RP))
2412 0 : pn4 = pn4->pn_kid;
2413 :
2414 : Value constVal;
2415 55129 : switch (pn4->getKind()) {
2416 : case PNK_NUMBER:
2417 4958 : constVal.setNumber(pn4->pn_dval);
2418 4958 : break;
2419 : case PNK_STRING:
2420 43027 : constVal.setString(pn4->pn_atom);
2421 43027 : break;
2422 : case PNK_TRUE:
2423 0 : constVal.setBoolean(true);
2424 0 : break;
2425 : case PNK_FALSE:
2426 0 : constVal.setBoolean(false);
2427 0 : break;
2428 : case PNK_NULL:
2429 113 : constVal.setNull();
2430 113 : break;
2431 : case PNK_NAME:
2432 2360 : if (!pn4->maybeExpr()) {
2433 2360 : ok = LookupCompileTimeConstant(cx, bce, pn4->pn_atom, &constVal);
2434 2360 : if (!ok)
2435 0 : goto release;
2436 2360 : if (!constVal.isMagic(JS_NO_CONSTANT)) {
2437 18 : if (constVal.isObject()) {
2438 : /*
2439 : * XXX JSOP_LOOKUPSWITCH does not support const-
2440 : * propagated object values, see bug 407186.
2441 : */
2442 0 : switchOp = JSOP_CONDSWITCH;
2443 0 : continue;
2444 : }
2445 18 : constPropagated = JS_TRUE;
2446 18 : break;
2447 : }
2448 : }
2449 : /* FALL THROUGH */
2450 : default:
2451 7013 : switchOp = JSOP_CONDSWITCH;
2452 7013 : continue;
2453 : }
2454 48116 : JS_ASSERT(constVal.isPrimitive());
2455 :
2456 48116 : pn3->pn_pval = AllocateSwitchConstant(cx);
2457 48116 : if (!pn3->pn_pval) {
2458 0 : ok = JS_FALSE;
2459 0 : goto release;
2460 : }
2461 :
2462 48116 : *pn3->pn_pval = constVal;
2463 :
2464 48116 : if (switchOp != JSOP_TABLESWITCH)
2465 34259 : continue;
2466 13857 : if (!pn3->pn_pval->isInt32()) {
2467 8881 : switchOp = JSOP_LOOKUPSWITCH;
2468 8881 : continue;
2469 : }
2470 4976 : i = pn3->pn_pval->toInt32();
2471 4976 : if ((unsigned)(i + (int)JS_BIT(15)) >= (unsigned)JS_BIT(16)) {
2472 0 : switchOp = JSOP_LOOKUPSWITCH;
2473 0 : continue;
2474 : }
2475 4976 : if (i < low)
2476 1572 : low = i;
2477 4976 : if (high < i)
2478 4552 : high = i;
2479 :
2480 : /*
2481 : * Check for duplicates, which require a JSOP_LOOKUPSWITCH.
2482 : * We bias i by 65536 if it's negative, and hope that's a rare
2483 : * case (because it requires a malloc'd bitmap).
2484 : */
2485 4976 : if (i < 0)
2486 47 : i += JS_BIT(16);
2487 4976 : if (i >= intmap_bitlen) {
2488 1424 : if (!intmap &&
2489 : i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) {
2490 1396 : intmap = intmap_space;
2491 1396 : intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2;
2492 : } else {
2493 : /* Just grab 8K for the worst-case bitmap. */
2494 28 : intmap_bitlen = JS_BIT(16);
2495 : intmap = (jsbitmap *)
2496 : cx->malloc_((JS_BIT(16) >> JS_BITS_PER_WORD_LOG2)
2497 28 : * sizeof(jsbitmap));
2498 28 : if (!intmap) {
2499 0 : JS_ReportOutOfMemory(cx);
2500 0 : return JS_FALSE;
2501 : }
2502 : }
2503 1424 : memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2);
2504 : }
2505 4976 : if (JS_TEST_BIT(intmap, i)) {
2506 0 : switchOp = JSOP_LOOKUPSWITCH;
2507 0 : continue;
2508 : }
2509 4976 : JS_SET_BIT(intmap, i);
2510 : }
2511 :
2512 : release:
2513 17150 : if (intmap && intmap != intmap_space)
2514 28 : cx->free_(intmap);
2515 17150 : if (!ok)
2516 0 : return JS_FALSE;
2517 :
2518 : /*
2519 : * Compute table length and select lookup instead if overlarge or
2520 : * more than half-sparse.
2521 : */
2522 17150 : if (switchOp == JSOP_TABLESWITCH) {
2523 1387 : tableLength = (uint32_t)(high - low + 1);
2524 1387 : if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
2525 160 : switchOp = JSOP_LOOKUPSWITCH;
2526 15763 : } else if (switchOp == JSOP_LOOKUPSWITCH) {
2527 : /*
2528 : * Lookup switch supports only atom indexes below 64K limit.
2529 : * Conservatively estimate the maximum possible index during
2530 : * switch generation and use conditional switch if it exceeds
2531 : * the limit.
2532 : */
2533 8750 : if (caseCount + bce->constList.length() > JS_BIT(16))
2534 0 : switchOp = JSOP_CONDSWITCH;
2535 : }
2536 : }
2537 :
2538 : /*
2539 : * Emit a note with two offsets: first tells total switch code length,
2540 : * second tells offset to first JSOP_CASE if condswitch.
2541 : */
2542 17231 : noteIndex = NewSrcNote3(cx, bce, SRC_SWITCH, 0, 0);
2543 17231 : if (noteIndex < 0)
2544 0 : return JS_FALSE;
2545 :
2546 17231 : if (switchOp == JSOP_CONDSWITCH) {
2547 : /*
2548 : * 0 bytes of immediate for unoptimized ECMAv2 switch.
2549 : */
2550 7013 : switchSize = 0;
2551 10218 : } else if (switchOp == JSOP_TABLESWITCH) {
2552 : /*
2553 : * 3 offsets (len, low, high) before the table, 1 per entry.
2554 : */
2555 1308 : switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
2556 : } else {
2557 : /*
2558 : * JSOP_LOOKUPSWITCH:
2559 : * 1 offset (len) and 1 atom index (npairs) before the table,
2560 : * 1 atom index and 1 jump offset per entry.
2561 : */
2562 : switchSize = (size_t)(JUMP_OFFSET_LEN + UINT16_LEN +
2563 8910 : (UINT32_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount);
2564 : }
2565 :
2566 : /* Emit switchOp followed by switchSize bytes of jump or lookup table. */
2567 17231 : if (EmitN(cx, bce, switchOp, switchSize) < 0)
2568 0 : return JS_FALSE;
2569 :
2570 17231 : off = -1;
2571 17231 : if (switchOp == JSOP_CONDSWITCH) {
2572 7013 : int caseNoteIndex = -1;
2573 7013 : JSBool beforeCases = JS_TRUE;
2574 :
2575 : /* Emit code for evaluating cases and jumping to case statements. */
2576 37871 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2577 30858 : pn4 = pn3->pn_left;
2578 30858 : if (pn4 && !EmitTree(cx, bce, pn4))
2579 0 : return JS_FALSE;
2580 30858 : if (caseNoteIndex >= 0) {
2581 : /* off is the previous JSOP_CASE's bytecode offset. */
2582 23457 : if (!SetSrcNoteOffset(cx, bce, (unsigned)caseNoteIndex, 0, bce->offset() - off))
2583 0 : return JS_FALSE;
2584 : }
2585 30858 : if (!pn4) {
2586 3776 : JS_ASSERT(pn3->isKind(PNK_DEFAULT));
2587 3776 : continue;
2588 : }
2589 27082 : caseNoteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
2590 27082 : if (caseNoteIndex < 0)
2591 0 : return JS_FALSE;
2592 27082 : off = EmitJump(cx, bce, JSOP_CASE, 0);
2593 27082 : if (off < 0)
2594 0 : return JS_FALSE;
2595 27082 : pn3->pn_offset = off;
2596 27082 : if (beforeCases) {
2597 : unsigned noteCount, noteCountDelta;
2598 :
2599 : /* Switch note's second offset is to first JSOP_CASE. */
2600 7013 : noteCount = bce->noteCount();
2601 7013 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, off - top))
2602 0 : return JS_FALSE;
2603 7013 : noteCountDelta = bce->noteCount() - noteCount;
2604 7013 : if (noteCountDelta != 0)
2605 0 : caseNoteIndex += noteCountDelta;
2606 7013 : beforeCases = JS_FALSE;
2607 : }
2608 : }
2609 :
2610 : /*
2611 : * If we didn't have an explicit default (which could fall in between
2612 : * cases, preventing us from fusing this SetSrcNoteOffset with the call
2613 : * in the loop above), link the last case to the implicit default for
2614 : * the decompiler.
2615 : */
2616 10250 : if (!hasDefault &&
2617 : caseNoteIndex >= 0 &&
2618 3237 : !SetSrcNoteOffset(cx, bce, (unsigned)caseNoteIndex, 0, bce->offset() - off))
2619 : {
2620 0 : return JS_FALSE;
2621 : }
2622 :
2623 : /* Emit default even if no explicit default statement. */
2624 7013 : defaultOffset = EmitJump(cx, bce, JSOP_DEFAULT, 0);
2625 7013 : if (defaultOffset < 0)
2626 0 : return JS_FALSE;
2627 : } else {
2628 10218 : pc = bce->code(top + JUMP_OFFSET_LEN);
2629 :
2630 10218 : if (switchOp == JSOP_TABLESWITCH) {
2631 : /* Fill in switch bounds, which we know fit in 16-bit offsets. */
2632 1308 : SET_JUMP_OFFSET(pc, low);
2633 1308 : pc += JUMP_OFFSET_LEN;
2634 1308 : SET_JUMP_OFFSET(pc, high);
2635 1308 : pc += JUMP_OFFSET_LEN;
2636 :
2637 : /*
2638 : * Use malloc to avoid arena bloat for programs with many switches.
2639 : * We free table if non-null at label out, so all control flow must
2640 : * exit this function through goto out or goto bad.
2641 : */
2642 1308 : if (tableLength != 0) {
2643 1227 : tableSize = (size_t)tableLength * sizeof *table;
2644 1227 : table = (ParseNode **) cx->malloc_(tableSize);
2645 1227 : if (!table)
2646 0 : return JS_FALSE;
2647 1227 : memset(table, 0, tableSize);
2648 6383 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2649 5156 : if (pn3->isKind(PNK_DEFAULT))
2650 1031 : continue;
2651 4125 : i = pn3->pn_pval->toInt32();
2652 4125 : i -= low;
2653 4125 : JS_ASSERT((uint32_t)i < tableLength);
2654 4125 : table[i] = pn3;
2655 : }
2656 : }
2657 : } else {
2658 8910 : JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH);
2659 :
2660 : /* Fill in the number of cases. */
2661 8910 : SET_UINT16(pc, caseCount);
2662 8910 : pc += UINT16_LEN;
2663 : }
2664 :
2665 : /*
2666 : * After this point, all control flow involving JSOP_TABLESWITCH
2667 : * must set ok and goto out to exit this function. To keep things
2668 : * simple, all switchOp cases exit that way.
2669 : */
2670 : MUST_FLOW_THROUGH("out");
2671 :
2672 10218 : if (constPropagated) {
2673 : /*
2674 : * Skip switchOp, as we are not setting jump offsets in the two
2675 : * for loops below. We'll restore bce->next() from savepc after,
2676 : * unless there was an error.
2677 : */
2678 18 : savepc = bce->next();
2679 18 : bce->current->next = pc + 1;
2680 18 : if (switchOp == JSOP_TABLESWITCH) {
2681 36 : for (i = 0; i < (int)tableLength; i++) {
2682 18 : pn3 = table[i];
2683 36 : if (pn3 &&
2684 : (pn4 = pn3->pn_left) != NULL &&
2685 18 : pn4->isKind(PNK_NAME))
2686 : {
2687 : /* Note a propagated constant with the const's name. */
2688 18 : JS_ASSERT(!pn4->maybeExpr());
2689 : jsatomid index;
2690 18 : if (!bce->makeAtomIndex(pn4->pn_atom, &index))
2691 0 : goto bad;
2692 18 : bce->current->next = pc;
2693 18 : if (NewSrcNote2(cx, bce, SRC_LABEL, ptrdiff_t(index)) < 0)
2694 0 : goto bad;
2695 : }
2696 18 : pc += JUMP_OFFSET_LEN;
2697 : }
2698 : } else {
2699 0 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2700 0 : pn4 = pn3->pn_left;
2701 0 : if (pn4 && pn4->isKind(PNK_NAME)) {
2702 : /* Note a propagated constant with the const's name. */
2703 0 : JS_ASSERT(!pn4->maybeExpr());
2704 : jsatomid index;
2705 0 : if (!bce->makeAtomIndex(pn4->pn_atom, &index))
2706 0 : goto bad;
2707 0 : bce->current->next = pc;
2708 0 : if (NewSrcNote2(cx, bce, SRC_LABEL, ptrdiff_t(index)) < 0)
2709 0 : goto bad;
2710 : }
2711 0 : pc += UINT32_INDEX_LEN + JUMP_OFFSET_LEN;
2712 : }
2713 : }
2714 18 : bce->current->next = savepc;
2715 : }
2716 : }
2717 :
2718 : /* Emit code for each case's statements, copying pn_offset up to pn3. */
2719 101784 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2720 84553 : if (switchOp == JSOP_CONDSWITCH && !pn3->isKind(PNK_DEFAULT))
2721 27082 : SetJumpOffsetAt(bce, pn3->pn_offset);
2722 84553 : pn4 = pn3->pn_right;
2723 84553 : ok = EmitTree(cx, bce, pn4);
2724 84553 : if (!ok)
2725 0 : goto out;
2726 84553 : pn3->pn_offset = pn4->pn_offset;
2727 84553 : if (pn3->isKind(PNK_DEFAULT))
2728 9513 : off = pn3->pn_offset - top;
2729 : }
2730 :
2731 17231 : if (!hasDefault) {
2732 : /* If no default case, offset for default is to end of switch. */
2733 7718 : off = bce->offset() - top;
2734 : }
2735 :
2736 : /* We better have set "off" by now. */
2737 17231 : JS_ASSERT(off != -1);
2738 :
2739 : /* Set the default offset (to end of switch if no default). */
2740 17231 : if (switchOp == JSOP_CONDSWITCH) {
2741 7013 : pc = NULL;
2742 7013 : JS_ASSERT(defaultOffset != -1);
2743 7013 : SET_JUMP_OFFSET(bce->code(defaultOffset), off - (defaultOffset - top));
2744 : } else {
2745 10218 : pc = bce->code(top);
2746 10218 : SET_JUMP_OFFSET(pc, off);
2747 10218 : pc += JUMP_OFFSET_LEN;
2748 : }
2749 :
2750 : /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
2751 17231 : off = bce->offset() - top;
2752 17231 : ok = SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, off);
2753 17231 : if (!ok)
2754 0 : goto out;
2755 :
2756 17231 : if (switchOp == JSOP_TABLESWITCH) {
2757 : /* Skip over the already-initialized switch bounds. */
2758 1308 : pc += 2 * JUMP_OFFSET_LEN;
2759 :
2760 : /* Fill in the jump table, if there is one. */
2761 5526 : for (i = 0; i < (int)tableLength; i++) {
2762 4218 : pn3 = table[i];
2763 4218 : off = pn3 ? pn3->pn_offset - top : 0;
2764 4218 : SET_JUMP_OFFSET(pc, off);
2765 4218 : pc += JUMP_OFFSET_LEN;
2766 : }
2767 15923 : } else if (switchOp == JSOP_LOOKUPSWITCH) {
2768 : /* Skip over the already-initialized number of cases. */
2769 8910 : pc += UINT16_LEN;
2770 :
2771 57413 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2772 48503 : if (pn3->isKind(PNK_DEFAULT))
2773 4670 : continue;
2774 43833 : if (!bce->constList.append(*pn3->pn_pval))
2775 0 : goto bad;
2776 43833 : SET_UINT32_INDEX(pc, bce->constList.length() - 1);
2777 43833 : pc += UINT32_INDEX_LEN;
2778 :
2779 43833 : off = pn3->pn_offset - top;
2780 43833 : SET_JUMP_OFFSET(pc, off);
2781 43833 : pc += JUMP_OFFSET_LEN;
2782 : }
2783 : }
2784 :
2785 : out:
2786 17231 : if (table)
2787 1227 : cx->free_(table);
2788 17231 : if (ok) {
2789 17231 : ok = PopStatementBCE(cx, bce);
2790 :
2791 : #if JS_HAS_BLOCK_SCOPE
2792 17231 : if (ok && pn->pn_right->isKind(PNK_LEXICALSCOPE))
2793 3521 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
2794 : #endif
2795 : }
2796 17231 : return ok;
2797 :
2798 : bad:
2799 0 : ok = JS_FALSE;
2800 0 : goto out;
2801 : }
2802 :
2803 : JSBool
2804 988449 : frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *body)
2805 : {
2806 : /*
2807 : * The decompiler has assumptions about what may occur immediately after
2808 : * script->main (e.g., in the case of destructuring params). Thus, put the
2809 : * following ops into the range [script->code, script->main). Note:
2810 : * execution starts from script->code, so this has no semantic effect.
2811 : */
2812 :
2813 988449 : if (bce->flags & TCF_FUN_IS_GENERATOR) {
2814 : /* JSOP_GENERATOR must be the first instruction. */
2815 3702 : bce->switchToProlog();
2816 3702 : JS_ASSERT(bce->next() == bce->base());
2817 3702 : if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
2818 0 : return false;
2819 3702 : bce->switchToMain();
2820 : }
2821 :
2822 : /*
2823 : * Strict mode functions' arguments objects copy initial parameter values.
2824 : * We create arguments objects lazily -- but that doesn't work for strict
2825 : * mode functions where a parameter might be modified and arguments might
2826 : * be accessed. For such functions we synthesize an access to arguments to
2827 : * initialize it with the original parameter values.
2828 : */
2829 988449 : if (bce->needsEagerArguments()) {
2830 297 : bce->switchToProlog();
2831 297 : if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0 || Emit1(cx, bce, JSOP_POP) < 0)
2832 0 : return false;
2833 297 : bce->switchToMain();
2834 : }
2835 :
2836 988449 : return EmitTree(cx, bce, body) &&
2837 988449 : Emit1(cx, bce, JSOP_STOP) >= 0 &&
2838 1976898 : JSScript::NewScriptFromEmitter(cx, bce);
2839 : }
2840 :
2841 : static bool
2842 1656827 : MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
2843 : jsatomid *result)
2844 : {
2845 : jsatomid atomIndex;
2846 :
2847 1656827 : if (!pn->pn_cookie.isFree()) {
2848 1343586 : atomIndex = pn->pn_cookie.slot();
2849 : } else {
2850 313241 : if (!bce->makeAtomIndex(pn->pn_atom, &atomIndex))
2851 0 : return false;
2852 : }
2853 :
2854 2283309 : if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
2855 313241 : (!bce->inFunction() || (bce->flags & TCF_FUN_HEAVYWEIGHT)) &&
2856 313241 : !(pn->pn_dflags & PND_GVAR))
2857 : {
2858 267299 : bce->switchToProlog();
2859 267299 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
2860 0 : return false;
2861 267299 : if (!EmitIndexOp(cx, prologOp, atomIndex, bce))
2862 0 : return false;
2863 267299 : bce->switchToMain();
2864 : }
2865 :
2866 4900816 : if (bce->inFunction() &&
2867 1187194 : JOF_OPTYPE(pn->getOp()) == JOF_LOCAL &&
2868 1186555 : pn->pn_cookie.slot() < bce->bindings.countVars() &&
2869 870240 : bce->shouldNoteClosedName(pn))
2870 : {
2871 80319 : if (!bce->closedVars.append(pn->pn_cookie.slot()))
2872 0 : return false;
2873 : }
2874 :
2875 1656827 : if (result)
2876 1630890 : *result = atomIndex;
2877 1656827 : return true;
2878 : }
2879 :
2880 : /*
2881 : * This enum tells EmitVariables and the destructuring functions how emit the
2882 : * given Parser::variables parse tree. In the base case, DefineVars, the caller
2883 : * only wants variables to be defined in the prologue (if necessary). For
2884 : * PushInitialValues, variable initializer expressions are evaluated and left
2885 : * on the stack. For InitializeVars, the initializer expressions values are
2886 : * assigned (to local variables) and popped.
2887 : */
2888 : enum VarEmitOption
2889 : {
2890 : DefineVars = 0,
2891 : PushInitialValues = 1,
2892 : InitializeVars = 2
2893 : };
2894 :
2895 : #if JS_HAS_DESTRUCTURING
2896 :
2897 : typedef JSBool
2898 : (*DestructuringDeclEmitter)(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn);
2899 :
2900 : static JSBool
2901 25937 : EmitDestructuringDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
2902 : {
2903 25937 : JS_ASSERT(pn->isKind(PNK_NAME));
2904 25937 : if (!BindNameToSlot(cx, bce, pn))
2905 0 : return JS_FALSE;
2906 :
2907 25937 : JS_ASSERT(!pn->isOp(JSOP_ARGUMENTS) && !pn->isOp(JSOP_CALLEE));
2908 25937 : return MaybeEmitVarDecl(cx, bce, prologOp, pn, NULL);
2909 : }
2910 :
2911 : static JSBool
2912 18769 : EmitDestructuringDecls(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
2913 : {
2914 : ParseNode *pn2, *pn3;
2915 : DestructuringDeclEmitter emitter;
2916 :
2917 18769 : if (pn->isKind(PNK_RB)) {
2918 45294 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2919 27641 : if (pn2->isKind(PNK_COMMA))
2920 691 : continue;
2921 26950 : emitter = (pn2->isKind(PNK_NAME))
2922 : ? EmitDestructuringDecl
2923 26950 : : EmitDestructuringDecls;
2924 26950 : if (!emitter(cx, bce, prologOp, pn2))
2925 0 : return JS_FALSE;
2926 : }
2927 : } else {
2928 1116 : JS_ASSERT(pn->isKind(PNK_RC));
2929 2944 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2930 1828 : pn3 = pn2->pn_right;
2931 1828 : emitter = pn3->isKind(PNK_NAME) ? EmitDestructuringDecl : EmitDestructuringDecls;
2932 1828 : if (!emitter(cx, bce, prologOp, pn3))
2933 0 : return JS_FALSE;
2934 : }
2935 : }
2936 18769 : return JS_TRUE;
2937 : }
2938 :
2939 : static JSBool
2940 : EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
2941 : VarEmitOption emitOption);
2942 :
2943 : /*
2944 : * EmitDestructuringLHS assumes the to-be-destructured value has been pushed on
2945 : * the stack and emits code to destructure a single lhs expression (either a
2946 : * name or a compound []/{} expression).
2947 : *
2948 : * If emitOption is InitializeVars, the to-be-destructured value is assigned to
2949 : * locals and ultimately the initial slot is popped (-1 total depth change).
2950 : *
2951 : * If emitOption is PushInitialValues, the to-be-destructured value is replaced
2952 : * with the initial values of the N (where 0 <= N) variables assigned in the
2953 : * lhs expression. (Same post-condition as EmitDestructuringOpsHelper)
2954 : */
2955 : static JSBool
2956 34555 : EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption)
2957 : {
2958 34555 : JS_ASSERT(emitOption != DefineVars);
2959 :
2960 : /*
2961 : * Now emit the lvalue opcode sequence. If the lvalue is a nested
2962 : * destructuring initialiser-form, call ourselves to handle it, then
2963 : * pop the matched value. Otherwise emit an lvalue bytecode sequence
2964 : * ending with a JSOP_ENUMELEM or equivalent op.
2965 : */
2966 34555 : if (pn->isKind(PNK_RB) || pn->isKind(PNK_RC)) {
2967 3192 : if (!EmitDestructuringOpsHelper(cx, bce, pn, emitOption))
2968 0 : return JS_FALSE;
2969 3192 : if (emitOption == InitializeVars) {
2970 : /*
2971 : * Per its post-condition, EmitDestructuringOpsHelper has left the
2972 : * to-be-destructured value on top of the stack.
2973 : */
2974 1644 : if (Emit1(cx, bce, JSOP_POP) < 0)
2975 0 : return JS_FALSE;
2976 : }
2977 : } else {
2978 31363 : if (emitOption == PushInitialValues) {
2979 : /*
2980 : * The lhs is a simple name so the to-be-destructured value is
2981 : * its initial value and there is nothing to do.
2982 : */
2983 900 : JS_ASSERT(pn->getOp() == JSOP_SETLOCAL);
2984 900 : JS_ASSERT(pn->pn_dflags & PND_BOUND);
2985 900 : return JS_TRUE;
2986 : }
2987 :
2988 : /* All paths below must pop after assigning to the lhs. */
2989 :
2990 30463 : if (pn->isKind(PNK_NAME)) {
2991 30337 : if (!BindNameToSlot(cx, bce, pn))
2992 0 : return JS_FALSE;
2993 30337 : if (pn->isConst() && !pn->isInitialized())
2994 0 : return Emit1(cx, bce, JSOP_POP) >= 0;
2995 : }
2996 :
2997 30463 : switch (pn->getOp()) {
2998 : case JSOP_SETNAME:
2999 : case JSOP_SETGNAME:
3000 : /*
3001 : * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless,
3002 : * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM.
3003 : * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp.
3004 : */
3005 1214 : if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, bce))
3006 0 : return JS_FALSE;
3007 1214 : break;
3008 :
3009 : case JSOP_SETCONST:
3010 13 : if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, bce))
3011 0 : return JS_FALSE;
3012 13 : break;
3013 :
3014 : case JSOP_SETLOCAL:
3015 : {
3016 28615 : uint16_t slot = pn->pn_cookie.slot();
3017 28615 : EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot);
3018 28615 : break;
3019 : }
3020 :
3021 : case JSOP_SETARG:
3022 : {
3023 621 : uint16_t slot = pn->pn_cookie.slot();
3024 621 : EMIT_UINT16_IMM_OP(pn->getOp(), slot);
3025 621 : if (Emit1(cx, bce, JSOP_POP) < 0)
3026 0 : return JS_FALSE;
3027 621 : break;
3028 : }
3029 :
3030 : default:
3031 : {
3032 : ptrdiff_t top;
3033 :
3034 0 : top = bce->offset();
3035 0 : if (!EmitTree(cx, bce, pn))
3036 0 : return JS_FALSE;
3037 0 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - top) < 0)
3038 0 : return JS_FALSE;
3039 0 : if (!EmitElemOpBase(cx, bce, JSOP_ENUMELEM))
3040 0 : return JS_FALSE;
3041 0 : break;
3042 : }
3043 :
3044 : case JSOP_ENUMELEM:
3045 0 : JS_ASSERT(0);
3046 : }
3047 : }
3048 :
3049 33655 : return JS_TRUE;
3050 : }
3051 :
3052 : /*
3053 : * Recursive helper for EmitDestructuringOps.
3054 : * EmitDestructuringOpsHelper assumes the to-be-destructured value has been
3055 : * pushed on the stack and emits code to destructure each part of a [] or {}
3056 : * lhs expression.
3057 : *
3058 : * If emitOption is InitializeVars, the initial to-be-destructured value is
3059 : * left untouched on the stack and the overall depth is not changed.
3060 : *
3061 : * If emitOption is PushInitialValues, the to-be-destructured value is replaced
3062 : * with the initial values of the N (where 0 <= N) variables assigned in the
3063 : * lhs expression. (Same post-condition as EmitDestructuringLHS)
3064 : */
3065 : static JSBool
3066 21505 : EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
3067 : VarEmitOption emitOption)
3068 : {
3069 21505 : JS_ASSERT(emitOption != DefineVars);
3070 :
3071 : unsigned index;
3072 : ParseNode *pn2, *pn3;
3073 : JSBool doElemOp;
3074 :
3075 : #ifdef DEBUG
3076 21505 : int stackDepth = bce->stackDepth;
3077 21505 : JS_ASSERT(stackDepth != 0);
3078 21505 : JS_ASSERT(pn->isArity(PN_LIST));
3079 21505 : JS_ASSERT(pn->isKind(PNK_RB) || pn->isKind(PNK_RC));
3080 : #endif
3081 :
3082 21505 : if (pn->pn_count == 0) {
3083 : /* Emit a DUP;POP sequence for the decompiler. */
3084 4518 : if (Emit1(cx, bce, JSOP_DUP) < 0 || Emit1(cx, bce, JSOP_POP) < 0)
3085 0 : return JS_FALSE;
3086 : }
3087 :
3088 21505 : index = 0;
3089 55449 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
3090 : /*
3091 : * Duplicate the value being destructured to use as a reference base.
3092 : * If dup is not the first one, annotate it for the decompiler.
3093 : */
3094 33944 : if (pn2 != pn->pn_head && NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
3095 0 : return JS_FALSE;
3096 33944 : if (Emit1(cx, bce, JSOP_DUP) < 0)
3097 0 : return JS_FALSE;
3098 :
3099 : /*
3100 : * Now push the property name currently being matched, which is either
3101 : * the array initialiser's current index, or the current property name
3102 : * "label" on the left of a colon in the object initialiser. Set pn3
3103 : * to the lvalue node, which is in the value-initializing position.
3104 : */
3105 33944 : doElemOp = JS_TRUE;
3106 33944 : if (pn->isKind(PNK_RB)) {
3107 31855 : if (!EmitNumberOp(cx, index, bce))
3108 0 : return JS_FALSE;
3109 31855 : pn3 = pn2;
3110 : } else {
3111 2089 : JS_ASSERT(pn->isKind(PNK_RC));
3112 2089 : JS_ASSERT(pn2->isKind(PNK_COLON));
3113 2089 : pn3 = pn2->pn_left;
3114 2089 : if (pn3->isKind(PNK_NUMBER)) {
3115 : /*
3116 : * If we are emitting an object destructuring initialiser,
3117 : * annotate the index op with SRC_INITPROP so we know we are
3118 : * not decompiling an array initialiser.
3119 : */
3120 108 : if (NewSrcNote(cx, bce, SRC_INITPROP) < 0)
3121 0 : return JS_FALSE;
3122 108 : if (!EmitNumberOp(cx, pn3->pn_dval, bce))
3123 0 : return JS_FALSE;
3124 : } else {
3125 1981 : JS_ASSERT(pn3->isKind(PNK_STRING) || pn3->isKind(PNK_NAME));
3126 1981 : if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, bce))
3127 0 : return JS_FALSE;
3128 1981 : doElemOp = JS_FALSE;
3129 : }
3130 2089 : pn3 = pn2->pn_right;
3131 : }
3132 :
3133 33944 : if (doElemOp) {
3134 : /*
3135 : * Ok, get the value of the matching property name. This leaves
3136 : * that value on top of the value being destructured, so the stack
3137 : * is one deeper than when we started.
3138 : */
3139 31963 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
3140 0 : return JS_FALSE;
3141 31963 : JS_ASSERT(bce->stackDepth >= stackDepth + 1);
3142 : }
3143 :
3144 : /* Nullary comma node makes a hole in the array destructurer. */
3145 33944 : if (pn3->isKind(PNK_COMMA) && pn3->isArity(PN_NULLARY)) {
3146 700 : JS_ASSERT(pn->isKind(PNK_RB));
3147 700 : JS_ASSERT(pn2 == pn3);
3148 700 : if (Emit1(cx, bce, JSOP_POP) < 0)
3149 0 : return JS_FALSE;
3150 : } else {
3151 33244 : int depthBefore = bce->stackDepth;
3152 33244 : if (!EmitDestructuringLHS(cx, bce, pn3, emitOption))
3153 0 : return JS_FALSE;
3154 :
3155 33244 : if (emitOption == PushInitialValues) {
3156 : /*
3157 : * After '[x,y]' in 'let ([[x,y], z] = o)', the stack is
3158 : * | to-be-decompiled-value | x | y |
3159 : * The goal is:
3160 : * | x | y | z |
3161 : * so emit a pick to produce the intermediate state
3162 : * | x | y | to-be-decompiled-value |
3163 : * before destructuring z. This gives the loop invariant that
3164 : * the to-be-compiled-value is always on top of the stack.
3165 : */
3166 : JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
3167 2448 : unsigned pickDistance = (unsigned)((bce->stackDepth + 1) - depthBefore);
3168 2448 : if (pickDistance > 0) {
3169 1278 : if (pickDistance > UINT8_MAX) {
3170 : ReportCompileErrorNumber(cx, bce->tokenStream(), pn3, JSREPORT_ERROR,
3171 0 : JSMSG_TOO_MANY_LOCALS);
3172 0 : return JS_FALSE;
3173 : }
3174 1278 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)pickDistance) < 0)
3175 0 : return false;
3176 : }
3177 : }
3178 : }
3179 :
3180 33944 : ++index;
3181 : }
3182 :
3183 21505 : if (emitOption == PushInitialValues) {
3184 : /*
3185 : * Per the above loop invariant, to-be-decompiled-value is at the top
3186 : * of the stack. To achieve the post-condition, pop it.
3187 : */
3188 5706 : if (Emit1(cx, bce, JSOP_POP) < 0)
3189 0 : return JS_FALSE;
3190 : }
3191 :
3192 21505 : return JS_TRUE;
3193 : }
3194 :
3195 : static ptrdiff_t
3196 10215 : OpToDeclType(JSOp op)
3197 : {
3198 10215 : switch (op) {
3199 : case JSOP_NOP:
3200 4253 : return SRC_DECL_LET;
3201 : case JSOP_DEFCONST:
3202 10 : return SRC_DECL_CONST;
3203 : case JSOP_DEFVAR:
3204 5631 : return SRC_DECL_VAR;
3205 : default:
3206 321 : return SRC_DECL_NONE;
3207 : }
3208 : }
3209 :
3210 : /*
3211 : * This utility accumulates a set of SRC_DESTRUCTLET notes which need to be
3212 : * backpatched with the offset from JSOP_DUP to JSOP_LET0.
3213 : *
3214 : * Also record whether the let head was a group assignment ([x,y] = [a,b])
3215 : * (which implies no SRC_DESTRUCTLET notes).
3216 : */
3217 : class LetNotes
3218 : {
3219 8901 : struct Pair {
3220 : ptrdiff_t dup;
3221 : unsigned index;
3222 4158 : Pair(ptrdiff_t dup, unsigned index) : dup(dup), index(index) {}
3223 : };
3224 : Vector<Pair> notes;
3225 : bool groupAssign;
3226 : DebugOnly<bool> updateCalled;
3227 :
3228 : public:
3229 45212 : LetNotes(JSContext *cx) : notes(cx), groupAssign(false), updateCalled(false) {}
3230 :
3231 90424 : ~LetNotes() {
3232 45212 : JS_ASSERT_IF(!notes.allocPolicy().context()->isExceptionPending(), updateCalled);
3233 45212 : }
3234 :
3235 180 : void setGroupAssign() {
3236 180 : JS_ASSERT(notes.empty());
3237 180 : groupAssign = true;
3238 180 : }
3239 :
3240 45212 : bool isGroupAssign() const {
3241 45212 : return groupAssign;
3242 : }
3243 :
3244 4158 : bool append(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t dup, unsigned index) {
3245 4158 : JS_ASSERT(!groupAssign);
3246 4158 : JS_ASSERT(SN_TYPE(bce->notes() + index) == SRC_DESTRUCTLET);
3247 4158 : if (!notes.append(Pair(dup, index)))
3248 0 : return false;
3249 :
3250 : /*
3251 : * Pessimistically inflate each srcnote. That way, there is no danger
3252 : * of inflation during update() (which would invalidate all indices).
3253 : */
3254 4158 : if (!SetSrcNoteOffset(cx, bce, index, 0, SN_MAX_OFFSET))
3255 0 : return false;
3256 4158 : JS_ASSERT(bce->notes()[index + 1] & SN_3BYTE_OFFSET_FLAG);
3257 4158 : return true;
3258 : }
3259 :
3260 : /* This should be called exactly once, right before JSOP_ENTERLET0. */
3261 45212 : bool update(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t offset) {
3262 45212 : JS_ASSERT(!updateCalled);
3263 49370 : for (size_t i = 0; i < notes.length(); ++i) {
3264 4158 : JS_ASSERT(offset > notes[i].dup);
3265 4158 : JS_ASSERT(*bce->code(notes[i].dup) == JSOP_DUP);
3266 4158 : JS_ASSERT(bce->notes()[notes[i].index + 1] & SN_3BYTE_OFFSET_FLAG);
3267 4158 : if (!SetSrcNoteOffset(cx, bce, notes[i].index, 0, offset - notes[i].dup))
3268 0 : return false;
3269 : }
3270 45212 : updateCalled = true;
3271 45212 : return true;
3272 : }
3273 : };
3274 :
3275 : static JSBool
3276 18313 : EmitDestructuringOps(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t declType, ParseNode *pn,
3277 : LetNotes *letNotes = NULL)
3278 : {
3279 : /*
3280 : * If we're called from a variable declaration, help the decompiler by
3281 : * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits.
3282 : * If the destructuring initialiser is empty, our helper will emit a
3283 : * JSOP_DUP followed by a JSOP_POP for the decompiler.
3284 : */
3285 18313 : if (letNotes) {
3286 4158 : ptrdiff_t index = NewSrcNote2(cx, bce, SRC_DESTRUCTLET, 0);
3287 4158 : if (index < 0 || !letNotes->append(cx, bce, bce->offset(), (unsigned)index))
3288 0 : return JS_FALSE;
3289 : } else {
3290 14155 : if (NewSrcNote2(cx, bce, SRC_DESTRUCT, declType) < 0)
3291 0 : return JS_FALSE;
3292 : }
3293 :
3294 : /*
3295 : * Call our recursive helper to emit the destructuring assignments and
3296 : * related stack manipulations.
3297 : */
3298 18313 : VarEmitOption emitOption = letNotes ? PushInitialValues : InitializeVars;
3299 18313 : return EmitDestructuringOpsHelper(cx, bce, pn, emitOption);
3300 : }
3301 :
3302 : static JSBool
3303 675 : EmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp,
3304 : ParseNode *lhs, ParseNode *rhs)
3305 : {
3306 : unsigned depth, limit, i, nslots;
3307 : ParseNode *pn;
3308 :
3309 675 : depth = limit = (unsigned) bce->stackDepth;
3310 1986 : for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
3311 1311 : if (limit == JS_BIT(16)) {
3312 : ReportCompileErrorNumber(cx, bce->tokenStream(), rhs, JSREPORT_ERROR,
3313 0 : JSMSG_ARRAY_INIT_TOO_BIG);
3314 0 : return JS_FALSE;
3315 : }
3316 :
3317 : /* MaybeEmitGroupAssignment won't call us if rhs is holey. */
3318 1311 : JS_ASSERT(!(pn->isKind(PNK_COMMA) && pn->isArity(PN_NULLARY)));
3319 1311 : if (!EmitTree(cx, bce, pn))
3320 0 : return JS_FALSE;
3321 1311 : ++limit;
3322 : }
3323 :
3324 675 : if (NewSrcNote2(cx, bce, SRC_GROUPASSIGN, OpToDeclType(prologOp)) < 0)
3325 0 : return JS_FALSE;
3326 :
3327 675 : i = depth;
3328 1986 : for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) {
3329 : /* MaybeEmitGroupAssignment requires lhs->pn_count <= rhs->pn_count. */
3330 1311 : JS_ASSERT(i < limit);
3331 1311 : int slot = AdjustBlockSlot(cx, bce, i);
3332 1311 : if (slot < 0)
3333 0 : return JS_FALSE;
3334 1311 : EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot);
3335 :
3336 1311 : if (pn->isKind(PNK_COMMA) && pn->isArity(PN_NULLARY)) {
3337 0 : if (Emit1(cx, bce, JSOP_POP) < 0)
3338 0 : return JS_FALSE;
3339 : } else {
3340 1311 : if (!EmitDestructuringLHS(cx, bce, pn, InitializeVars))
3341 0 : return JS_FALSE;
3342 : }
3343 : }
3344 :
3345 675 : nslots = limit - depth;
3346 675 : EMIT_UINT16_IMM_OP(JSOP_POPN, nslots);
3347 675 : bce->stackDepth = (unsigned) depth;
3348 675 : return JS_TRUE;
3349 : }
3350 :
3351 : /*
3352 : * Helper called with pop out param initialized to a JSOP_POP* opcode. If we
3353 : * can emit a group assignment sequence, which results in 0 stack depth delta,
3354 : * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop.
3355 : */
3356 : static JSBool
3357 2307736 : MaybeEmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
3358 : JSOp *pop)
3359 : {
3360 2307736 : JS_ASSERT(pn->isKind(PNK_ASSIGN));
3361 2307736 : JS_ASSERT(pn->isOp(JSOP_NOP));
3362 2307736 : JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
3363 :
3364 2307736 : ParseNode *lhs = pn->pn_left;
3365 2307736 : ParseNode *rhs = pn->pn_right;
3366 2308411 : if (lhs->isKind(PNK_RB) && rhs->isKind(PNK_RB) &&
3367 675 : !(rhs->pn_xflags & PNX_HOLEY) &&
3368 : lhs->pn_count <= rhs->pn_count) {
3369 675 : if (!EmitGroupAssignment(cx, bce, prologOp, lhs, rhs))
3370 0 : return JS_FALSE;
3371 675 : *pop = JSOP_NOP;
3372 : }
3373 2307736 : return JS_TRUE;
3374 : }
3375 :
3376 : /*
3377 : * Like MaybeEmitGroupAssignment, but for 'let ([x,y] = [a,b]) ...'.
3378 : *
3379 : * Instead of issuing a sequence |dup|eval-rhs|set-lhs|pop| (which doesn't work
3380 : * since the bound vars don't yet have slots), just eval/push each rhs element
3381 : * just like what EmitLet would do for 'let (x = a, y = b) ...'. While shorter,
3382 : * simpler and more efficient than MaybeEmitGroupAssignment, it is harder to
3383 : * decompile so we restrict the ourselves to cases where the lhs and rhs are in
3384 : * 1:1 correspondence and lhs elements are simple names.
3385 : */
3386 : static bool
3387 1026 : MaybeEmitLetGroupDecl(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
3388 : LetNotes *letNotes, JSOp *pop)
3389 : {
3390 1026 : JS_ASSERT(pn->isKind(PNK_ASSIGN));
3391 1026 : JS_ASSERT(pn->isOp(JSOP_NOP));
3392 1026 : JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
3393 :
3394 1026 : ParseNode *lhs = pn->pn_left;
3395 1026 : ParseNode *rhs = pn->pn_right;
3396 1782 : if (lhs->isKind(PNK_RB) && rhs->isKind(PNK_RB) &&
3397 378 : !(rhs->pn_xflags & PNX_HOLEY) &&
3398 378 : !(lhs->pn_xflags & PNX_HOLEY) &&
3399 : lhs->pn_count == rhs->pn_count)
3400 : {
3401 666 : for (ParseNode *l = lhs->pn_head; l; l = l->pn_next) {
3402 486 : if (l->getOp() != JSOP_SETLOCAL)
3403 162 : return true;
3404 : }
3405 :
3406 468 : for (ParseNode *r = rhs->pn_head; r; r = r->pn_next) {
3407 288 : if (!EmitTree(cx, bce, r))
3408 0 : return false;
3409 : }
3410 :
3411 180 : letNotes->setGroupAssign();
3412 180 : *pop = JSOP_NOP;
3413 : }
3414 864 : return true;
3415 : }
3416 :
3417 : #endif /* JS_HAS_DESTRUCTURING */
3418 :
3419 : static JSBool
3420 1326954 : EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption,
3421 : LetNotes *letNotes = NULL)
3422 : {
3423 1326954 : JS_ASSERT(pn->isArity(PN_LIST));
3424 1326954 : JS_ASSERT(!!letNotes == (emitOption == PushInitialValues));
3425 :
3426 1326954 : ptrdiff_t off = -1, noteIndex = -1;
3427 : ParseNode *next;
3428 1647352 : for (ParseNode *pn2 = pn->pn_head; ; pn2 = next) {
3429 1647352 : bool first = pn2 == pn->pn_head;
3430 1647352 : next = pn2->pn_next;
3431 :
3432 : ParseNode *pn3;
3433 1647352 : if (!pn2->isKind(PNK_NAME)) {
3434 : #if JS_HAS_DESTRUCTURING
3435 23910 : if (pn2->isKind(PNK_RB) || pn2->isKind(PNK_RC)) {
3436 : /*
3437 : * Emit variable binding ops, but not destructuring ops. The
3438 : * parser (see Parser::variables) has ensured that our caller
3439 : * will be the PNK_FOR/PNK_FORIN case in EmitTree, and that
3440 : * case will emit the destructuring code only after emitting an
3441 : * enumerating opcode and a branch that tests whether the
3442 : * enumeration ended.
3443 : */
3444 2968 : JS_ASSERT(emitOption == DefineVars);
3445 2968 : JS_ASSERT(pn->pn_count == 1);
3446 2968 : if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn2))
3447 0 : return JS_FALSE;
3448 2968 : break;
3449 : }
3450 : #endif
3451 :
3452 : /*
3453 : * A destructuring initialiser assignment preceded by var will
3454 : * never occur to the left of 'in' in a for-in loop. As with 'for
3455 : * (var x = i in o)...', this will cause the entire 'var [a, b] =
3456 : * i' to be hoisted out of the loop.
3457 : */
3458 20942 : JS_ASSERT(pn2->isKind(PNK_ASSIGN));
3459 20942 : JS_ASSERT(pn2->isOp(JSOP_NOP));
3460 20942 : JS_ASSERT(emitOption != DefineVars);
3461 :
3462 : /*
3463 : * To allow the front end to rewrite var f = x; as f = x; when a
3464 : * function f(){} precedes the var, detect simple name assignment
3465 : * here and initialize the name.
3466 : */
3467 : #if !JS_HAS_DESTRUCTURING
3468 : JS_ASSERT(pn2->pn_left->isKind(PNK_NAME));
3469 : #else
3470 20942 : if (pn2->pn_left->isKind(PNK_NAME))
3471 : #endif
3472 : {
3473 7448 : pn3 = pn2->pn_right;
3474 7448 : pn2 = pn2->pn_left;
3475 7448 : goto do_name;
3476 : }
3477 :
3478 : #if JS_HAS_DESTRUCTURING
3479 13494 : ptrdiff_t stackDepthBefore = bce->stackDepth;
3480 13494 : JSOp op = JSOP_POP;
3481 13494 : if (pn->pn_count == 1) {
3482 : /*
3483 : * If this is the only destructuring assignment in the list,
3484 : * try to optimize to a group assignment. If we're in a let
3485 : * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP
3486 : * in pn->pn_op, to suppress a second (and misplaced) 'let'.
3487 : */
3488 9822 : JS_ASSERT(noteIndex < 0 && !pn2->pn_next);
3489 9822 : if (letNotes) {
3490 1026 : if (!MaybeEmitLetGroupDecl(cx, bce, pn2, letNotes, &op))
3491 0 : return JS_FALSE;
3492 : } else {
3493 8796 : if (!MaybeEmitGroupAssignment(cx, bce, pn->getOp(), pn2, &op))
3494 0 : return JS_FALSE;
3495 : }
3496 : }
3497 13494 : if (op == JSOP_NOP) {
3498 534 : pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT;
3499 : } else {
3500 12960 : pn3 = pn2->pn_left;
3501 12960 : if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn3))
3502 0 : return JS_FALSE;
3503 :
3504 12960 : if (!EmitTree(cx, bce, pn2->pn_right))
3505 0 : return JS_FALSE;
3506 :
3507 : /* Only the first list element should print 'let' or 'var'. */
3508 : ptrdiff_t declType = pn2 == pn->pn_head
3509 9540 : ? OpToDeclType(pn->getOp())
3510 22500 : : SRC_DECL_NONE;
3511 :
3512 12960 : if (!EmitDestructuringOps(cx, bce, declType, pn3, letNotes))
3513 0 : return JS_FALSE;
3514 : }
3515 13494 : ptrdiff_t stackDepthAfter = bce->stackDepth;
3516 :
3517 : /* Give let ([] = x) a slot (see CheckDestructuring). */
3518 13494 : JS_ASSERT(stackDepthBefore <= stackDepthAfter);
3519 13494 : if (letNotes && stackDepthBefore == stackDepthAfter) {
3520 3690 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
3521 0 : return JS_FALSE;
3522 : }
3523 :
3524 : /* If we are not initializing, nothing to pop. */
3525 13494 : if (emitOption != InitializeVars) {
3526 4338 : if (next)
3527 3159 : continue;
3528 1179 : break;
3529 : }
3530 9156 : goto emit_note_pop;
3531 : #endif
3532 : }
3533 :
3534 : /*
3535 : * Load initializer early to share code above that jumps to do_name.
3536 : * NB: if this var redeclares an existing binding, then pn2 is linked
3537 : * on its definition's use-chain and pn_expr has been overlayed with
3538 : * pn_lexdef.
3539 : */
3540 1623442 : pn3 = pn2->maybeExpr();
3541 :
3542 : do_name:
3543 1630890 : if (!BindNameToSlot(cx, bce, pn2))
3544 0 : return JS_FALSE;
3545 :
3546 : JSOp op;
3547 : jsatomid atomIndex;
3548 :
3549 1630890 : op = pn2->getOp();
3550 1630890 : if (op == JSOP_ARGUMENTS) {
3551 : /* JSOP_ARGUMENTS => no initializer */
3552 0 : JS_ASSERT(!pn3 && !letNotes);
3553 0 : pn3 = NULL;
3554 0 : atomIndex = 0;
3555 : } else {
3556 1630890 : JS_ASSERT(op != JSOP_CALLEE);
3557 1630890 : JS_ASSERT(!pn2->pn_cookie.isFree() || !pn->isOp(JSOP_NOP));
3558 1630890 : if (!MaybeEmitVarDecl(cx, bce, pn->getOp(), pn2, &atomIndex))
3559 0 : return JS_FALSE;
3560 :
3561 1630890 : if (pn3) {
3562 1245384 : JS_ASSERT(emitOption != DefineVars);
3563 1245384 : JS_ASSERT_IF(emitOption == PushInitialValues, op == JSOP_SETLOCAL);
3564 1245384 : if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
3565 156087 : JSOp bindOp = (op == JSOP_SETNAME) ? JSOP_BINDNAME : JSOP_BINDGNAME;
3566 156087 : if (!EmitIndex32(cx, bindOp, atomIndex, bce))
3567 0 : return false;
3568 : }
3569 1414148 : if (pn->isOp(JSOP_DEFCONST) &&
3570 168764 : !DefineCompileTimeConstant(cx, bce, pn2->pn_atom, pn3))
3571 : {
3572 0 : return JS_FALSE;
3573 : }
3574 :
3575 1245384 : unsigned oldflags = bce->flags;
3576 1245384 : bce->flags &= ~TCF_IN_FOR_INIT;
3577 1245384 : if (!EmitTree(cx, bce, pn3))
3578 0 : return JS_FALSE;
3579 1245384 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
3580 385506 : } else if (letNotes) {
3581 : /* JSOP_ENTERLETx expects at least 1 slot to have been pushed. */
3582 148078 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
3583 0 : return JS_FALSE;
3584 : }
3585 : }
3586 :
3587 : /* If we are not initializing, nothing to pop. */
3588 1630890 : if (emitOption != InitializeVars) {
3589 240804 : if (next)
3590 147788 : continue;
3591 93016 : break;
3592 : }
3593 :
3594 1390086 : JS_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr);
3595 2610946 : if (first && NewSrcNote2(cx, bce, SRC_DECL,
3596 1220860 : (pn->isOp(JSOP_DEFCONST))
3597 : ? SRC_DECL_CONST
3598 1052101 : : (pn->isOp(JSOP_DEFVAR))
3599 : ? SRC_DECL_VAR
3600 2272961 : : SRC_DECL_LET) < 0) {
3601 0 : return JS_FALSE;
3602 : }
3603 1390086 : if (op == JSOP_ARGUMENTS) {
3604 0 : if (!EmitArguments(cx, bce))
3605 0 : return JS_FALSE;
3606 1390086 : } else if (!pn2->pn_cookie.isFree()) {
3607 1078214 : EMIT_UINT16_IMM_OP(op, atomIndex);
3608 : } else {
3609 311872 : if (!EmitIndexOp(cx, op, atomIndex, bce))
3610 0 : return false;
3611 : }
3612 :
3613 : #if JS_HAS_DESTRUCTURING
3614 : emit_note_pop:
3615 : #endif
3616 1399242 : ptrdiff_t tmp = bce->offset();
3617 1399242 : if (noteIndex >= 0) {
3618 169451 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp-off))
3619 0 : return JS_FALSE;
3620 : }
3621 1399242 : if (!next)
3622 1229791 : break;
3623 169451 : off = tmp;
3624 169451 : noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
3625 169451 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_POP) < 0)
3626 0 : return JS_FALSE;
3627 : }
3628 :
3629 1326954 : if (pn->pn_xflags & PNX_POPVAR) {
3630 1196300 : if (Emit1(cx, bce, JSOP_POP) < 0)
3631 0 : return JS_FALSE;
3632 : }
3633 :
3634 1326954 : return JS_TRUE;
3635 : }
3636 :
3637 : static bool
3638 3073274 : EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, ParseNode *rhs)
3639 : {
3640 3073274 : ptrdiff_t top = bce->offset();
3641 :
3642 : /*
3643 : * Check left operand type and generate specialized code for it.
3644 : * Specialize to avoid ECMA "reference type" values on the operand
3645 : * stack, which impose pervasive runtime "GetValue" costs.
3646 : */
3647 3073274 : jsatomid atomIndex = (jsatomid) -1; /* quell GCC overwarning */
3648 3073274 : jsbytecode offset = 1;
3649 :
3650 3073274 : switch (lhs->getKind()) {
3651 : case PNK_NAME:
3652 708871 : if (!BindNameToSlot(cx, bce, lhs))
3653 0 : return false;
3654 708871 : if (!lhs->pn_cookie.isFree()) {
3655 571748 : JS_ASSERT(lhs->pn_cookie.level() == 0);
3656 571748 : atomIndex = lhs->pn_cookie.slot();
3657 : } else {
3658 137123 : if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
3659 0 : return false;
3660 137123 : if (!lhs->isConst()) {
3661 137123 : JSOp op = lhs->isOp(JSOP_SETGNAME) ? JSOP_BINDGNAME : JSOP_BINDNAME;
3662 137123 : if (!EmitIndex32(cx, op, atomIndex, bce))
3663 0 : return false;
3664 137123 : offset++;
3665 : }
3666 : }
3667 708871 : break;
3668 : case PNK_DOT:
3669 648208 : if (!EmitTree(cx, bce, lhs->expr()))
3670 0 : return false;
3671 648208 : offset++;
3672 648208 : if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
3673 0 : return false;
3674 648208 : break;
3675 : case PNK_LB:
3676 1710806 : JS_ASSERT(lhs->isArity(PN_BINARY));
3677 1710806 : if (!EmitTree(cx, bce, lhs->pn_left))
3678 0 : return false;
3679 1710806 : if (!EmitTree(cx, bce, lhs->pn_right))
3680 0 : return false;
3681 1710806 : offset += 2;
3682 1710806 : break;
3683 : #if JS_HAS_DESTRUCTURING
3684 : case PNK_RB:
3685 : case PNK_RC:
3686 5353 : break;
3687 : #endif
3688 : case PNK_LP:
3689 18 : if (!EmitTree(cx, bce, lhs))
3690 0 : return false;
3691 18 : offset++;
3692 18 : break;
3693 : #if JS_HAS_XML_SUPPORT
3694 : case PNK_XMLUNARY:
3695 18 : JS_ASSERT(!bce->inStrictMode());
3696 18 : JS_ASSERT(lhs->isOp(JSOP_SETXMLNAME));
3697 18 : if (!EmitTree(cx, bce, lhs->pn_kid))
3698 0 : return false;
3699 18 : if (Emit1(cx, bce, JSOP_BINDXMLNAME) < 0)
3700 0 : return false;
3701 18 : offset++;
3702 18 : break;
3703 : #endif
3704 : default:
3705 0 : JS_ASSERT(0);
3706 : }
3707 :
3708 3073274 : if (op != JSOP_NOP) {
3709 81366 : JS_ASSERT(rhs);
3710 81366 : switch (lhs->getKind()) {
3711 : case PNK_NAME:
3712 73848 : if (lhs->isConst()) {
3713 0 : if (lhs->isOp(JSOP_CALLEE)) {
3714 0 : if (Emit1(cx, bce, JSOP_CALLEE) < 0)
3715 0 : return false;
3716 0 : } else if (lhs->isOp(JSOP_NAME) || lhs->isOp(JSOP_GETGNAME)) {
3717 0 : if (!EmitIndex32(cx, lhs->getOp(), atomIndex, bce))
3718 0 : return false;
3719 : } else {
3720 0 : JS_ASSERT(JOF_OPTYPE(lhs->getOp()) != JOF_ATOM);
3721 0 : EMIT_UINT16_IMM_OP(lhs->getOp(), atomIndex);
3722 : }
3723 73848 : } else if (lhs->isOp(JSOP_SETNAME)) {
3724 10144 : if (Emit1(cx, bce, JSOP_DUP) < 0)
3725 0 : return false;
3726 10144 : if (!EmitIndex32(cx, JSOP_GETXPROP, atomIndex, bce))
3727 0 : return false;
3728 63704 : } else if (lhs->isOp(JSOP_SETGNAME)) {
3729 21745 : if (!BindGlobal(cx, bce, lhs, lhs->pn_atom))
3730 0 : return false;
3731 21745 : if (!EmitAtomOp(cx, lhs, JSOP_GETGNAME, bce))
3732 0 : return false;
3733 : } else {
3734 41959 : EMIT_UINT16_IMM_OP(lhs->isOp(JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL, atomIndex);
3735 : }
3736 73848 : break;
3737 : case PNK_DOT:
3738 6639 : if (Emit1(cx, bce, JSOP_DUP) < 0)
3739 0 : return false;
3740 6639 : if (lhs->pn_atom == cx->runtime->atomState.protoAtom) {
3741 0 : if (!EmitIndex32(cx, JSOP_QNAMEPART, atomIndex, bce))
3742 0 : return false;
3743 0 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
3744 0 : return false;
3745 : } else {
3746 6639 : bool isLength = (lhs->pn_atom == cx->runtime->atomState.lengthAtom);
3747 6639 : if (!EmitIndex32(cx, isLength ? JSOP_LENGTH : JSOP_GETPROP, atomIndex, bce))
3748 0 : return false;
3749 : }
3750 6639 : break;
3751 : case PNK_LB:
3752 : case PNK_LP:
3753 : #if JS_HAS_XML_SUPPORT
3754 : case PNK_XMLUNARY:
3755 : #endif
3756 879 : if (Emit1(cx, bce, JSOP_DUP2) < 0)
3757 0 : return false;
3758 879 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
3759 0 : return false;
3760 879 : break;
3761 : default:;
3762 : }
3763 : }
3764 :
3765 : /* Now emit the right operand (it may affect the namespace). */
3766 3073274 : if (rhs) {
3767 3018658 : if (!EmitTree(cx, bce, rhs))
3768 0 : return false;
3769 : } else {
3770 : /* The value to assign is the next enumeration value in a for-in loop. */
3771 54616 : if (Emit2(cx, bce, JSOP_ITERNEXT, offset) < 0)
3772 0 : return false;
3773 : }
3774 :
3775 : /* If += etc., emit the binary operator with a decompiler note. */
3776 3073274 : if (op != JSOP_NOP) {
3777 : /*
3778 : * Take care to avoid SRC_ASSIGNOP if the left-hand side is a const
3779 : * declared in the current compilation unit, as in this case (just
3780 : * a bit further below) we will avoid emitting the assignment op.
3781 : */
3782 81366 : if (!lhs->isKind(PNK_NAME) || !lhs->isConst()) {
3783 81366 : if (NewSrcNote(cx, bce, SRC_ASSIGNOP) < 0)
3784 0 : return false;
3785 : }
3786 81366 : if (Emit1(cx, bce, op) < 0)
3787 0 : return false;
3788 : }
3789 :
3790 : /* Left parts such as a.b.c and a[b].c need a decompiler note. */
3791 10155977 : if (!lhs->isKind(PNK_NAME) &&
3792 : #if JS_HAS_DESTRUCTURING
3793 2364403 : !lhs->isKind(PNK_RB) &&
3794 2359250 : !lhs->isKind(PNK_RC) &&
3795 : #endif
3796 2359050 : NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - top) < 0)
3797 : {
3798 0 : return false;
3799 : }
3800 :
3801 : /* Finally, emit the specialized assignment bytecode. */
3802 3073274 : switch (lhs->getKind()) {
3803 : case PNK_NAME:
3804 708871 : if (lhs->isConst()) {
3805 0 : if (!rhs) {
3806 : ReportCompileErrorNumber(cx, bce->tokenStream(), lhs, JSREPORT_ERROR,
3807 0 : JSMSG_BAD_FOR_LEFTSIDE);
3808 0 : return false;
3809 : }
3810 0 : break;
3811 : }
3812 708871 : if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL)) {
3813 571748 : JS_ASSERT(atomIndex < UINT16_MAX);
3814 571748 : EMIT_UINT16_IMM_OP(lhs->getOp(), atomIndex);
3815 : } else {
3816 137123 : if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
3817 0 : return false;
3818 : }
3819 708871 : break;
3820 : case PNK_DOT:
3821 648208 : if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
3822 0 : return false;
3823 648208 : break;
3824 : case PNK_LB:
3825 : case PNK_LP:
3826 1710824 : if (Emit1(cx, bce, JSOP_SETELEM) < 0)
3827 0 : return false;
3828 1710824 : break;
3829 : #if JS_HAS_DESTRUCTURING
3830 : case PNK_RB:
3831 : case PNK_RC:
3832 5353 : if (!EmitDestructuringOps(cx, bce, SRC_DECL_NONE, lhs))
3833 0 : return false;
3834 5353 : break;
3835 : #endif
3836 : #if JS_HAS_XML_SUPPORT
3837 : case PNK_XMLUNARY:
3838 18 : JS_ASSERT(!bce->inStrictMode());
3839 18 : if (Emit1(cx, bce, JSOP_SETXMLNAME) < 0)
3840 0 : return false;
3841 18 : break;
3842 : #endif
3843 : default:
3844 0 : JS_ASSERT(0);
3845 : }
3846 3073274 : return true;
3847 : }
3848 :
3849 : #ifdef DEBUG
3850 : static JSBool
3851 28589 : GettableNoteForNextOp(BytecodeEmitter *bce)
3852 : {
3853 : ptrdiff_t offset, target;
3854 : jssrcnote *sn, *end;
3855 :
3856 28589 : offset = 0;
3857 28589 : target = bce->offset();
3858 4110963 : for (sn = bce->notes(), end = sn + bce->noteCount(); sn < end;
3859 4082374 : sn = SN_NEXT(sn)) {
3860 4082374 : if (offset == target && SN_IS_GETTABLE(sn))
3861 0 : return JS_TRUE;
3862 4082374 : offset += SN_DELTA(sn);
3863 : }
3864 28589 : return JS_FALSE;
3865 : }
3866 : #endif
3867 :
3868 : /* Top-level named functions need a nop for decompilation. */
3869 : static JSBool
3870 226586 : EmitFunctionDefNop(JSContext *cx, BytecodeEmitter *bce, unsigned index)
3871 : {
3872 226586 : return NewSrcNote2(cx, bce, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 &&
3873 226586 : Emit1(cx, bce, JSOP_NOP) >= 0;
3874 : }
3875 :
3876 : static bool
3877 227156 : EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
3878 : {
3879 227156 : const size_t len = 1 + UINT32_INDEX_LEN;
3880 227156 : ptrdiff_t offset = EmitCheck(cx, bce, len);
3881 227156 : if (offset < 0)
3882 0 : return false;
3883 :
3884 227156 : jsbytecode *next = bce->next();
3885 227156 : next[0] = JSOP_NEWINIT;
3886 227156 : next[1] = jsbytecode(key);
3887 227156 : next[2] = 0;
3888 227156 : next[3] = 0;
3889 227156 : next[4] = 0;
3890 227156 : bce->current->next = next + len;
3891 227156 : UpdateDepth(cx, bce, offset);
3892 227156 : CheckTypeSet(cx, bce, JSOP_NEWINIT);
3893 227156 : return true;
3894 : }
3895 :
3896 : bool
3897 29743 : ParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp)
3898 : {
3899 29743 : switch (getKind()) {
3900 : case PNK_NUMBER:
3901 13988 : vp->setNumber(pn_dval);
3902 13988 : return true;
3903 : case PNK_STRING:
3904 6643 : vp->setString(pn_atom);
3905 6643 : return true;
3906 : case PNK_TRUE:
3907 509 : vp->setBoolean(true);
3908 509 : return true;
3909 : case PNK_FALSE:
3910 342 : vp->setBoolean(false);
3911 342 : return true;
3912 : case PNK_NULL:
3913 81 : vp->setNull();
3914 81 : return true;
3915 : case PNK_RB: {
3916 5767 : JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
3917 :
3918 5767 : JSObject *obj = NewDenseAllocatedArray(cx, pn_count);
3919 5767 : if (!obj)
3920 0 : return false;
3921 :
3922 5767 : unsigned idx = 0;
3923 22900 : for (ParseNode *pn = pn_head; pn; idx++, pn = pn->pn_next) {
3924 : Value value;
3925 17133 : if (!pn->getConstantValue(cx, strictChecks, &value))
3926 0 : return false;
3927 17133 : if (!obj->defineGeneric(cx, INT_TO_JSID(idx), value, NULL, NULL, JSPROP_ENUMERATE))
3928 0 : return false;
3929 : }
3930 5767 : JS_ASSERT(idx == pn_count);
3931 :
3932 5767 : types::FixArrayType(cx, obj);
3933 5767 : vp->setObject(*obj);
3934 5767 : return true;
3935 : }
3936 : case PNK_RC: {
3937 2413 : JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
3938 :
3939 2413 : gc::AllocKind kind = GuessObjectGCKind(pn_count);
3940 2413 : JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
3941 2413 : if (!obj)
3942 0 : return false;
3943 :
3944 7747 : for (ParseNode *pn = pn_head; pn; pn = pn->pn_next) {
3945 : Value value;
3946 5334 : if (!pn->pn_right->getConstantValue(cx, strictChecks, &value))
3947 0 : return false;
3948 :
3949 5334 : ParseNode *pnid = pn->pn_left;
3950 5334 : if (pnid->isKind(PNK_NUMBER)) {
3951 126 : Value idvalue = NumberValue(pnid->pn_dval);
3952 : jsid id;
3953 126 : if (idvalue.isInt32() && INT_FITS_IN_JSID(idvalue.toInt32()))
3954 99 : id = INT_TO_JSID(idvalue.toInt32());
3955 27 : else if (!js_InternNonIntElementId(cx, obj, idvalue, &id))
3956 0 : return false;
3957 126 : if (!obj->defineGeneric(cx, id, value, NULL, NULL, JSPROP_ENUMERATE))
3958 0 : return false;
3959 : } else {
3960 5208 : JS_ASSERT(pnid->isKind(PNK_NAME) || pnid->isKind(PNK_STRING));
3961 5208 : JS_ASSERT(pnid->pn_atom != cx->runtime->atomState.protoAtom);
3962 5208 : jsid id = ATOM_TO_JSID(pnid->pn_atom);
3963 5208 : if (!DefineNativeProperty(cx, obj, id, value, NULL, NULL,
3964 5208 : JSPROP_ENUMERATE, 0, 0)) {
3965 0 : return false;
3966 : }
3967 : }
3968 : }
3969 :
3970 2413 : types::FixObjectType(cx, obj);
3971 2413 : vp->setObject(*obj);
3972 2413 : return true;
3973 : }
3974 : default:
3975 0 : JS_NOT_REACHED("Unexpected node");
3976 : }
3977 : return false;
3978 : }
3979 :
3980 : static bool
3981 7276 : EmitSingletonInitialiser(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
3982 : {
3983 : Value value;
3984 7276 : if (!pn->getConstantValue(cx, bce->needStrictChecks(), &value))
3985 0 : return false;
3986 :
3987 7276 : JS_ASSERT(value.isObject());
3988 7276 : ObjectBox *objbox = bce->parser->newObjectBox(&value.toObject());
3989 7276 : if (!objbox)
3990 0 : return false;
3991 :
3992 7276 : return EmitObjectOp(cx, objbox, JSOP_OBJECT, bce);
3993 : }
3994 :
3995 : /* See the SRC_FOR source note offsetBias comments later in this file. */
3996 : JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
3997 : JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1);
3998 :
3999 : class EmitLevelManager
4000 : {
4001 : BytecodeEmitter *bce;
4002 : public:
4003 59282303 : EmitLevelManager(BytecodeEmitter *bce) : bce(bce) { bce->emitLevel++; }
4004 59282303 : ~EmitLevelManager() { bce->emitLevel--; }
4005 : };
4006 :
4007 : static bool
4008 225154 : EmitCatch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4009 : {
4010 : ptrdiff_t catchStart, guardJump;
4011 :
4012 : /*
4013 : * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
4014 : * and save the block object atom.
4015 : */
4016 225154 : StmtInfo *stmt = bce->topStmt;
4017 225154 : JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE));
4018 225154 : stmt->type = STMT_CATCH;
4019 225154 : catchStart = stmt->update;
4020 :
4021 : /* Go up one statement info record to the TRY or FINALLY record. */
4022 225154 : stmt = stmt->down;
4023 225154 : JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);
4024 :
4025 : /* Pick up the pending exception and bind it to the catch variable. */
4026 225154 : if (Emit1(cx, bce, JSOP_EXCEPTION) < 0)
4027 0 : return false;
4028 :
4029 : /*
4030 : * Dup the exception object if there is a guard for rethrowing to use
4031 : * it later when rethrowing or in other catches.
4032 : */
4033 225154 : if (pn->pn_kid2 && Emit1(cx, bce, JSOP_DUP) < 0)
4034 0 : return false;
4035 :
4036 225154 : ParseNode *pn2 = pn->pn_kid1;
4037 225154 : switch (pn2->getKind()) {
4038 : #if JS_HAS_DESTRUCTURING
4039 : case PNK_RB:
4040 : case PNK_RC:
4041 0 : if (!EmitDestructuringOps(cx, bce, SRC_DECL_NONE, pn2))
4042 0 : return false;
4043 0 : if (Emit1(cx, bce, JSOP_POP) < 0)
4044 0 : return false;
4045 0 : break;
4046 : #endif
4047 :
4048 : case PNK_NAME:
4049 : /* Inline and specialize BindNameToSlot for pn2. */
4050 225154 : JS_ASSERT(!pn2->pn_cookie.isFree());
4051 225154 : EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie.asInteger());
4052 225154 : break;
4053 :
4054 : default:
4055 0 : JS_ASSERT(0);
4056 : }
4057 :
4058 : /* Emit the guard expression, if there is one. */
4059 225154 : if (pn->pn_kid2) {
4060 1217 : if (!EmitTree(cx, bce, pn->pn_kid2))
4061 0 : return false;
4062 1217 : if (!SetSrcNoteOffset(cx, bce, CATCHNOTE(*stmt), 0, bce->offset() - catchStart))
4063 0 : return false;
4064 : /* ifeq <next block> */
4065 1217 : guardJump = EmitJump(cx, bce, JSOP_IFEQ, 0);
4066 1217 : if (guardJump < 0)
4067 0 : return false;
4068 1217 : GUARDJUMP(*stmt) = guardJump;
4069 :
4070 : /* Pop duplicated exception object as we no longer need it. */
4071 1217 : if (Emit1(cx, bce, JSOP_POP) < 0)
4072 0 : return false;
4073 : }
4074 :
4075 : /* Emit the catch body. */
4076 225154 : if (!EmitTree(cx, bce, pn->pn_kid3))
4077 0 : return false;
4078 :
4079 : /*
4080 : * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via
4081 : * our PNK_LEXICALSCOPE parent, so the decompiler knows to pop.
4082 : */
4083 225154 : ptrdiff_t off = bce->stackDepth;
4084 225154 : if (NewSrcNote2(cx, bce, SRC_CATCH, off) < 0)
4085 0 : return false;
4086 225154 : return true;
4087 : }
4088 :
4089 : /*
4090 : * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127. See
4091 : * the comment on EmitSwitch.
4092 : */
4093 : MOZ_NEVER_INLINE static bool
4094 241021 : EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4095 : {
4096 : StmtInfo stmtInfo;
4097 241021 : ptrdiff_t catchJump = -1;
4098 :
4099 : /*
4100 : * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
4101 : * for later fixup.
4102 : *
4103 : * When a finally block is active (STMT_FINALLY in our tree context),
4104 : * non-local jumps (including jumps-over-catches) result in a GOSUB
4105 : * being written into the bytecode stream and fixed-up later (c.f.
4106 : * EmitBackPatchOp and BackPatch).
4107 : */
4108 241021 : PushStatement(bce, &stmtInfo, pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, bce->offset());
4109 :
4110 : /*
4111 : * Since an exception can be thrown at any place inside the try block,
4112 : * we need to restore the stack and the scope chain before we transfer
4113 : * the control to the exception handler.
4114 : *
4115 : * For that we store in a try note associated with the catch or
4116 : * finally block the stack depth upon the try entry. The interpreter
4117 : * uses this depth to properly unwind the stack and the scope chain.
4118 : */
4119 241021 : int depth = bce->stackDepth;
4120 :
4121 : /* Mark try location for decompilation, then emit try block. */
4122 241021 : if (Emit1(cx, bce, JSOP_TRY) < 0)
4123 0 : return false;
4124 241021 : ptrdiff_t tryStart = bce->offset();
4125 241021 : if (!EmitTree(cx, bce, pn->pn_kid1))
4126 0 : return false;
4127 241021 : JS_ASSERT(depth == bce->stackDepth);
4128 :
4129 : /* GOSUB to finally, if present. */
4130 241021 : if (pn->pn_kid3) {
4131 22053 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4132 0 : return false;
4133 22053 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &GOSUBS(stmtInfo)) < 0)
4134 0 : return false;
4135 : }
4136 :
4137 : /* Emit (hidden) jump over catch and/or finally. */
4138 241021 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4139 0 : return false;
4140 241021 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &catchJump) < 0)
4141 0 : return false;
4142 :
4143 241021 : ptrdiff_t tryEnd = bce->offset();
4144 :
4145 : /* If this try has a catch block, emit it. */
4146 241021 : ParseNode *lastCatch = NULL;
4147 241021 : if (ParseNode *pn2 = pn->pn_kid2) {
4148 224923 : unsigned count = 0; /* previous catch block's population */
4149 :
4150 : /*
4151 : * The emitted code for a catch block looks like:
4152 : *
4153 : * [throwing] only if 2nd+ catch block
4154 : * [leaveblock] only if 2nd+ catch block
4155 : * enterblock with SRC_CATCH
4156 : * exception
4157 : * [dup] only if catchguard
4158 : * setlocalpop <slot> or destructuring code
4159 : * [< catchguard code >] if there's a catchguard
4160 : * [ifeq <offset to next catch block>] " "
4161 : * [pop] only if catchguard
4162 : * < catch block contents >
4163 : * leaveblock
4164 : * goto <end of catch blocks> non-local; finally applies
4165 : *
4166 : * If there's no catch block without a catchguard, the last
4167 : * <offset to next catch block> points to rethrow code. This
4168 : * code will [gosub] to the finally code if appropriate, and is
4169 : * also used for the catch-all trynote for capturing exceptions
4170 : * thrown from catch{} blocks.
4171 : */
4172 450077 : for (ParseNode *pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
4173 : ptrdiff_t guardJump, catchNote;
4174 :
4175 225154 : JS_ASSERT(bce->stackDepth == depth);
4176 225154 : guardJump = GUARDJUMP(stmtInfo);
4177 225154 : if (guardJump != -1) {
4178 : /* Fix up and clean up previous catch block. */
4179 231 : SetJumpOffsetAt(bce, guardJump);
4180 :
4181 : /*
4182 : * Account for JSOP_ENTERBLOCK (whose block object count
4183 : * is saved below) and pushed exception object that we
4184 : * still have after the jumping from the previous guard.
4185 : */
4186 231 : bce->stackDepth = depth + count + 1;
4187 :
4188 : /*
4189 : * Move exception back to cx->exception to prepare for
4190 : * the next catch. We hide [throwing] from the decompiler
4191 : * since it compensates for the hidden JSOP_DUP at the
4192 : * start of the previous guarded catch.
4193 : */
4194 462 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0 ||
4195 231 : Emit1(cx, bce, JSOP_THROWING) < 0) {
4196 0 : return false;
4197 : }
4198 231 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4199 0 : return false;
4200 231 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
4201 231 : JS_ASSERT(bce->stackDepth == depth);
4202 : }
4203 :
4204 : /*
4205 : * Annotate the JSOP_ENTERBLOCK that's about to be generated
4206 : * by the call to EmitTree immediately below. Save this
4207 : * source note's index in stmtInfo for use by the PNK_CATCH:
4208 : * case, where the length of the catch guard is set as the
4209 : * note's offset.
4210 : */
4211 225154 : catchNote = NewSrcNote2(cx, bce, SRC_CATCH, 0);
4212 225154 : if (catchNote < 0)
4213 0 : return false;
4214 225154 : CATCHNOTE(stmtInfo) = catchNote;
4215 :
4216 : /*
4217 : * Emit the lexical scope and catch body. Save the catch's
4218 : * block object population via count, for use when targeting
4219 : * guardJump at the next catch (the guard mismatch case).
4220 : */
4221 225154 : JS_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
4222 225154 : count = pn3->pn_objbox->object->asStaticBlock().slotCount();
4223 225154 : if (!EmitTree(cx, bce, pn3))
4224 0 : return false;
4225 :
4226 : /* gosub <finally>, if required */
4227 225154 : if (pn->pn_kid3) {
4228 5955 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &GOSUBS(stmtInfo)) < 0)
4229 0 : return false;
4230 5955 : JS_ASSERT(bce->stackDepth == depth);
4231 : }
4232 :
4233 : /*
4234 : * Jump over the remaining catch blocks. This will get fixed
4235 : * up to jump to after catch/finally.
4236 : */
4237 225154 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4238 0 : return false;
4239 225154 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &catchJump) < 0)
4240 0 : return false;
4241 :
4242 : /*
4243 : * Save a pointer to the last catch node to handle try-finally
4244 : * and try-catch(guard)-finally special cases.
4245 : */
4246 225154 : lastCatch = pn3->expr();
4247 : }
4248 : }
4249 :
4250 : /*
4251 : * Last catch guard jumps to the rethrow code sequence if none of the
4252 : * guards match. Target guardJump at the beginning of the rethrow
4253 : * sequence, just in case a guard expression throws and leaves the
4254 : * stack unbalanced.
4255 : */
4256 241021 : if (lastCatch && lastCatch->pn_kid2) {
4257 986 : SetJumpOffsetAt(bce, GUARDJUMP(stmtInfo));
4258 :
4259 : /* Sync the stack to take into account pushed exception. */
4260 986 : JS_ASSERT(bce->stackDepth == depth);
4261 986 : bce->stackDepth = depth + 1;
4262 :
4263 : /*
4264 : * Rethrow the exception, delegating executing of finally if any
4265 : * to the exception handler.
4266 : */
4267 986 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0 || Emit1(cx, bce, JSOP_THROW) < 0)
4268 0 : return false;
4269 : }
4270 :
4271 241021 : JS_ASSERT(bce->stackDepth == depth);
4272 :
4273 : /* Emit finally handler if any. */
4274 241021 : ptrdiff_t finallyStart = 0; /* to quell GCC uninitialized warnings */
4275 241021 : if (pn->pn_kid3) {
4276 : /*
4277 : * Fix up the gosubs that might have been emitted before non-local
4278 : * jumps to the finally code.
4279 : */
4280 22053 : if (!BackPatch(cx, bce, GOSUBS(stmtInfo), bce->next(), JSOP_GOSUB))
4281 0 : return false;
4282 :
4283 22053 : finallyStart = bce->offset();
4284 :
4285 : /* Indicate that we're emitting a subroutine body. */
4286 22053 : stmtInfo.type = STMT_SUBROUTINE;
4287 22053 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_kid3->pn_pos.begin.lineno))
4288 0 : return false;
4289 66159 : if (Emit1(cx, bce, JSOP_FINALLY) < 0 ||
4290 22053 : !EmitTree(cx, bce, pn->pn_kid3) ||
4291 22053 : Emit1(cx, bce, JSOP_RETSUB) < 0)
4292 : {
4293 0 : return false;
4294 : }
4295 22053 : JS_ASSERT(bce->stackDepth == depth);
4296 : }
4297 241021 : if (!PopStatementBCE(cx, bce))
4298 0 : return false;
4299 :
4300 241021 : if (NewSrcNote(cx, bce, SRC_ENDBRACE) < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
4301 0 : return false;
4302 :
4303 : /* Fix up the end-of-try/catch jumps to come here. */
4304 241021 : if (!BackPatch(cx, bce, catchJump, bce->next(), JSOP_GOTO))
4305 0 : return false;
4306 :
4307 : /*
4308 : * Add the try note last, to let post-order give us the right ordering
4309 : * (first to last for a given nesting level, inner to outer by level).
4310 : */
4311 241021 : if (pn->pn_kid2 && !NewTryNote(cx, bce, JSTRY_CATCH, depth, tryStart, tryEnd))
4312 0 : return false;
4313 :
4314 : /*
4315 : * If we've got a finally, mark try+catch region with additional
4316 : * trynote to catch exceptions (re)thrown from a catch block or
4317 : * for the try{}finally{} case.
4318 : */
4319 241021 : if (pn->pn_kid3 && !NewTryNote(cx, bce, JSTRY_FINALLY, depth, tryStart, finallyStart))
4320 0 : return false;
4321 :
4322 241021 : return true;
4323 : }
4324 :
4325 : static bool
4326 929543 : EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4327 : {
4328 : StmtInfo stmtInfo;
4329 :
4330 : /* Initialize so we can detect else-if chains and avoid recursion. */
4331 929543 : stmtInfo.type = STMT_IF;
4332 929543 : ptrdiff_t beq = -1;
4333 929543 : ptrdiff_t jmp = -1;
4334 929543 : ptrdiff_t noteIndex = -1;
4335 :
4336 : if_again:
4337 : /* Emit code for the condition before pushing stmtInfo. */
4338 949320 : if (!EmitTree(cx, bce, pn->pn_kid1))
4339 0 : return JS_FALSE;
4340 949320 : ptrdiff_t top = bce->offset();
4341 949320 : if (stmtInfo.type == STMT_IF) {
4342 929543 : PushStatement(bce, &stmtInfo, STMT_IF, top);
4343 : } else {
4344 : /*
4345 : * We came here from the goto further below that detects else-if
4346 : * chains, so we must mutate stmtInfo back into a STMT_IF record.
4347 : * Also (see below for why) we need a note offset for SRC_IF_ELSE
4348 : * to help the decompiler. Actually, we need two offsets, one for
4349 : * decompiling any else clause and the second for decompiling an
4350 : * else-if chain without bracing, overindenting, or incorrectly
4351 : * scoping let declarations.
4352 : */
4353 19777 : JS_ASSERT(stmtInfo.type == STMT_ELSE);
4354 19777 : stmtInfo.type = STMT_IF;
4355 19777 : stmtInfo.update = top;
4356 19777 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq))
4357 0 : return JS_FALSE;
4358 19777 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 1, top - beq))
4359 0 : return JS_FALSE;
4360 : }
4361 :
4362 : /* Emit an annotated branch-if-false around the then part. */
4363 949320 : ParseNode *pn3 = pn->pn_kid3;
4364 949320 : noteIndex = NewSrcNote(cx, bce, pn3 ? SRC_IF_ELSE : SRC_IF);
4365 949320 : if (noteIndex < 0)
4366 0 : return JS_FALSE;
4367 949320 : beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
4368 949320 : if (beq < 0)
4369 0 : return JS_FALSE;
4370 :
4371 : /* Emit code for the then and optional else parts. */
4372 949320 : if (!EmitTree(cx, bce, pn->pn_kid2))
4373 0 : return JS_FALSE;
4374 949320 : if (pn3) {
4375 : /* Modify stmtInfo so we know we're in the else part. */
4376 156612 : stmtInfo.type = STMT_ELSE;
4377 :
4378 : /*
4379 : * Emit a JSOP_BACKPATCH op to jump from the end of our then part
4380 : * around the else part. The PopStatementBCE call at the bottom of
4381 : * this function will fix up the backpatch chain linked from
4382 : * stmtInfo.breaks.
4383 : */
4384 156612 : jmp = EmitGoto(cx, bce, &stmtInfo, &stmtInfo.breaks);
4385 156612 : if (jmp < 0)
4386 0 : return JS_FALSE;
4387 :
4388 : /* Ensure the branch-if-false comes here, then emit the else. */
4389 156612 : SetJumpOffsetAt(bce, beq);
4390 156612 : if (pn3->isKind(PNK_IF)) {
4391 19777 : pn = pn3;
4392 19777 : goto if_again;
4393 : }
4394 :
4395 136835 : if (!EmitTree(cx, bce, pn3))
4396 0 : return JS_FALSE;
4397 :
4398 : /*
4399 : * Annotate SRC_IF_ELSE with the offset from branch to jump, for
4400 : * the decompiler's benefit. We can't just "back up" from the pc
4401 : * of the else clause, because we don't know whether an extended
4402 : * jump was required to leap from the end of the then clause over
4403 : * the else clause.
4404 : */
4405 136835 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq))
4406 0 : return JS_FALSE;
4407 : } else {
4408 : /* No else part, fixup the branch-if-false to come here. */
4409 792708 : SetJumpOffsetAt(bce, beq);
4410 : }
4411 929543 : return PopStatementBCE(cx, bce);
4412 : }
4413 :
4414 : #if JS_HAS_BLOCK_SCOPE
4415 : /*
4416 : * pnLet represents one of:
4417 : *
4418 : * let-expression: (let (x = y) EXPR)
4419 : * let-statement: let (x = y) { ... }
4420 : *
4421 : * For a let-expression 'let (x = a, [y,z] = b) e', EmitLet produces:
4422 : *
4423 : * bytecode stackDepth srcnotes
4424 : * evaluate a +1
4425 : * evaluate b +1
4426 : * dup +1 SRC_DESTRUCTLET + offset to enterlet0
4427 : * destructure y
4428 : * pick 1
4429 : * dup +1 SRC_DESTRUCTLET + offset to enterlet0
4430 : * pick
4431 : * destructure z
4432 : * pick 1
4433 : * pop -1
4434 : * enterlet0 SRC_DECL + offset to leaveblockexpr
4435 : * evaluate e +1
4436 : * leaveblockexpr -3 SRC_PCBASE + offset to evaluate a
4437 : *
4438 : * Note that, since enterlet0 simply changes fp->blockChain and does not
4439 : * otherwise touch the stack, evaluation of the let-var initializers must leave
4440 : * the initial value in the let-var's future slot.
4441 : *
4442 : * The SRC_DESTRUCTLET distinguish JSOP_DUP as the beginning of a destructuring
4443 : * let initialization and the offset allows the decompiler to find the block
4444 : * object from which to find let var names. These forward offsets require
4445 : * backpatching, which is handled by LetNotes.
4446 : *
4447 : * The SRC_DECL offset allows recursive decompilation of 'e'.
4448 : *
4449 : * The SRC_PCBASE allows js_DecompileValueGenerator to walk backwards from
4450 : * JSOP_LEAVEBLOCKEXPR to the beginning of the let and is only needed for
4451 : * let-expressions.
4452 : */
4453 : /*
4454 : * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127. See
4455 : * the comment on EmitSwitch.
4456 : */
4457 : MOZ_NEVER_INLINE static bool
4458 45212 : EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
4459 : {
4460 45212 : JS_ASSERT(pnLet->isArity(PN_BINARY));
4461 45212 : ParseNode *varList = pnLet->pn_left;
4462 45212 : JS_ASSERT(varList->isArity(PN_LIST));
4463 45212 : ParseNode *letBody = pnLet->pn_right;
4464 45212 : JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
4465 45212 : StaticBlockObject &blockObj = letBody->pn_objbox->object->asStaticBlock();
4466 :
4467 45212 : ptrdiff_t letHeadOffset = bce->offset();
4468 45212 : int letHeadDepth = bce->stackDepth;
4469 :
4470 90424 : LetNotes letNotes(cx);
4471 45212 : if (!EmitVariables(cx, bce, varList, PushInitialValues, &letNotes))
4472 0 : return false;
4473 :
4474 : /* Push storage for hoisted let decls (e.g. 'let (x) { let y }'). */
4475 45212 : uint32_t alreadyPushed = unsigned(bce->stackDepth - letHeadDepth);
4476 45212 : uint32_t blockObjCount = blockObj.slotCount();
4477 56947 : for (uint32_t i = alreadyPushed; i < blockObjCount; ++i) {
4478 : /* Tell the decompiler not to print the decl in the let head. */
4479 11735 : if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
4480 0 : return false;
4481 11735 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
4482 0 : return false;
4483 : }
4484 :
4485 : StmtInfo stmtInfo;
4486 45212 : PushBlockScope(bce, &stmtInfo, blockObj, bce->offset());
4487 :
4488 45212 : if (!letNotes.update(cx, bce, bce->offset()))
4489 0 : return false;
4490 :
4491 45212 : ptrdiff_t declNote = NewSrcNote(cx, bce, SRC_DECL);
4492 45212 : if (declNote < 0)
4493 0 : return false;
4494 :
4495 45212 : ptrdiff_t bodyBegin = bce->offset();
4496 45212 : if (!EmitEnterBlock(cx, bce, letBody, JSOP_ENTERLET0))
4497 0 : return false;
4498 :
4499 45212 : if (!EmitTree(cx, bce, letBody->pn_expr))
4500 0 : return false;
4501 :
4502 45212 : JSOp leaveOp = letBody->getOp();
4503 45212 : if (leaveOp == JSOP_LEAVEBLOCKEXPR) {
4504 1538 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - letHeadOffset) < 0)
4505 0 : return false;
4506 : }
4507 :
4508 45212 : JS_ASSERT(leaveOp == JSOP_LEAVEBLOCK || leaveOp == JSOP_LEAVEBLOCKEXPR);
4509 45212 : EMIT_UINT16_IMM_OP(leaveOp, blockObj.slotCount());
4510 :
4511 45212 : ptrdiff_t bodyEnd = bce->offset();
4512 45212 : JS_ASSERT(bodyEnd > bodyBegin);
4513 :
4514 45212 : if (!PopStatementBCE(cx, bce))
4515 0 : return false;
4516 :
4517 : ptrdiff_t o = PackLetData((bodyEnd - bodyBegin) -
4518 : (JSOP_ENTERLET0_LENGTH + JSOP_LEAVEBLOCK_LENGTH),
4519 45212 : letNotes.isGroupAssign());
4520 45212 : return SetSrcNoteOffset(cx, bce, declNote, 0, o);
4521 : }
4522 : #endif
4523 :
4524 : #if JS_HAS_XML_SUPPORT
4525 : static bool
4526 36 : EmitXMLTag(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4527 : {
4528 36 : JS_ASSERT(!bce->inStrictMode());
4529 :
4530 36 : if (Emit1(cx, bce, JSOP_STARTXML) < 0)
4531 0 : return false;
4532 :
4533 : {
4534 : jsatomid index;
4535 36 : JSAtom *tagAtom = (pn->isKind(PNK_XMLETAGO))
4536 : ? cx->runtime->atomState.etagoAtom
4537 36 : : cx->runtime->atomState.stagoAtom;
4538 36 : if (!bce->makeAtomIndex(tagAtom, &index))
4539 0 : return false;
4540 36 : if (!EmitIndex32(cx, JSOP_STRING, index, bce))
4541 0 : return false;
4542 : }
4543 :
4544 36 : JS_ASSERT(pn->pn_count != 0);
4545 36 : ParseNode *pn2 = pn->pn_head;
4546 36 : if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0)
4547 0 : return false;
4548 36 : if (!EmitTree(cx, bce, pn2))
4549 0 : return false;
4550 36 : if (Emit1(cx, bce, JSOP_ADD) < 0)
4551 0 : return false;
4552 :
4553 : uint32_t i;
4554 36 : for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) {
4555 0 : if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0)
4556 0 : return false;
4557 0 : if (!EmitTree(cx, bce, pn2))
4558 0 : return false;
4559 0 : if ((i & 1) && pn2->isKind(PNK_XMLCURLYEXPR)) {
4560 0 : if (Emit1(cx, bce, JSOP_TOATTRVAL) < 0)
4561 0 : return false;
4562 : }
4563 0 : if (Emit1(cx, bce, (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0)
4564 0 : return false;
4565 : }
4566 :
4567 : {
4568 : jsatomid index;
4569 36 : JSAtom *tmp = (pn->isKind(PNK_XMLPTAGC)) ? cx->runtime->atomState.ptagcAtom
4570 36 : : cx->runtime->atomState.tagcAtom;
4571 36 : if (!bce->makeAtomIndex(tmp, &index))
4572 0 : return false;
4573 36 : if (!EmitIndex32(cx, JSOP_STRING, index, bce))
4574 0 : return false;
4575 : }
4576 36 : if (Emit1(cx, bce, JSOP_ADD) < 0)
4577 0 : return false;
4578 :
4579 36 : if ((pn->pn_xflags & PNX_XMLROOT) && Emit1(cx, bce, pn->getOp()) < 0)
4580 0 : return false;
4581 :
4582 36 : return true;
4583 : }
4584 :
4585 : static bool
4586 0 : EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, XMLProcessingInstruction &pi)
4587 : {
4588 0 : JS_ASSERT(!bce->inStrictMode());
4589 :
4590 : jsatomid index;
4591 0 : if (!bce->makeAtomIndex(pi.data(), &index))
4592 0 : return false;
4593 0 : if (!EmitIndex32(cx, JSOP_QNAMEPART, index, bce))
4594 0 : return false;
4595 0 : if (!EmitAtomOp(cx, pi.target(), JSOP_XMLPI, bce))
4596 0 : return false;
4597 0 : return true;
4598 : }
4599 : #endif
4600 :
4601 : /*
4602 : * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127. See
4603 : * the comment on EmitSwitch.
4604 : */
4605 : MOZ_NEVER_INLINE static bool
4606 366540 : EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4607 : {
4608 366540 : JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
4609 366540 : JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK);
4610 :
4611 : StmtInfo stmtInfo;
4612 366540 : ObjectBox *objbox = pn->pn_objbox;
4613 366540 : StaticBlockObject &blockObj = objbox->object->asStaticBlock();
4614 366540 : PushBlockScope(bce, &stmtInfo, blockObj, bce->offset());
4615 :
4616 : /*
4617 : * For compound statements (i.e. { stmt-list }), the decompiler does not
4618 : * emit curlies by default. However, if this stmt-list contains a let
4619 : * declaration, this is semantically invalid so we need to add a srcnote to
4620 : * enterblock to tell the decompiler to add curlies. This condition
4621 : * shouldn't be so complicated; try to find a simpler condition.
4622 : */
4623 366540 : ptrdiff_t noteIndex = -1;
4624 752875 : if (pn->expr()->getKind() != PNK_FOR &&
4625 353685 : pn->expr()->getKind() != PNK_CATCH &&
4626 : (stmtInfo.down
4627 : ? stmtInfo.down->type == STMT_BLOCK &&
4628 32578 : (!stmtInfo.down->down || stmtInfo.down->down->type != STMT_FOR_IN_LOOP)
4629 72 : : !bce->inFunction()))
4630 : {
4631 : /* There must be no source note already output for the next op. */
4632 85956 : JS_ASSERT(bce->noteCount() == 0 ||
4633 : bce->lastNoteOffset() != bce->offset() ||
4634 85956 : !GettableNoteForNextOp(bce));
4635 28724 : noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0);
4636 28724 : if (noteIndex < 0)
4637 0 : return false;
4638 : }
4639 :
4640 366540 : ptrdiff_t bodyBegin = bce->offset();
4641 366540 : if (!EmitEnterBlock(cx, bce, pn, JSOP_ENTERBLOCK))
4642 0 : return false;
4643 :
4644 366540 : if (!EmitTree(cx, bce, pn->pn_expr))
4645 0 : return false;
4646 :
4647 366540 : if (noteIndex >= 0) {
4648 28724 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, bce->offset() - bodyBegin))
4649 0 : return false;
4650 : }
4651 :
4652 366540 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount());
4653 :
4654 366540 : return PopStatementBCE(cx, bce);
4655 : }
4656 :
4657 : static bool
4658 630 : EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4659 : {
4660 : StmtInfo stmtInfo;
4661 630 : if (!EmitTree(cx, bce, pn->pn_left))
4662 0 : return false;
4663 630 : PushStatement(bce, &stmtInfo, STMT_WITH, bce->offset());
4664 630 : if (Emit1(cx, bce, JSOP_ENTERWITH) < 0)
4665 0 : return false;
4666 :
4667 630 : if (!EmitTree(cx, bce, pn->pn_right))
4668 0 : return false;
4669 630 : if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
4670 0 : return false;
4671 630 : return PopStatementBCE(cx, bce);
4672 : }
4673 :
4674 : static bool
4675 54616 : EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
4676 : {
4677 : StmtInfo stmtInfo;
4678 54616 : PushStatement(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
4679 :
4680 54616 : ParseNode *forHead = pn->pn_left;
4681 54616 : ParseNode *forBody = pn->pn_right;
4682 :
4683 54616 : ParseNode *pn1 = forHead->pn_kid1;
4684 54616 : bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
4685 54616 : JS_ASSERT_IF(letDecl, pn1->isLet());
4686 :
4687 54616 : StaticBlockObject *blockObj = letDecl ? &pn1->pn_objbox->object->asStaticBlock() : NULL;
4688 54616 : uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
4689 :
4690 54616 : if (letDecl) {
4691 : /*
4692 : * The let's slot(s) will be under the iterator, but the block must not
4693 : * be entered (i.e. fp->blockChain set) until after evaluating the rhs.
4694 : * Thus, push to reserve space and enterblock after. The same argument
4695 : * applies when leaving the loop. Thus, a for-let-in loop looks like:
4696 : *
4697 : * push x N
4698 : * eval rhs
4699 : * iter
4700 : * enterlet1
4701 : * goto
4702 : * ... loop body
4703 : * ifne
4704 : * leaveforinlet
4705 : * enditer
4706 : * popn(N)
4707 : */
4708 65056 : for (uint32_t i = 0; i < blockObjCount; ++i) {
4709 33676 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
4710 0 : return false;
4711 : }
4712 : }
4713 :
4714 : /*
4715 : * If the left part is 'var x', emit code to define x if necessary
4716 : * using a prolog opcode, but do not emit a pop. If the left part was
4717 : * originally 'var x = i', the parser will have rewritten it; see
4718 : * Parser::forStatement. 'for (let x = i in o)' is mercifully banned.
4719 : */
4720 54616 : if (pn1) {
4721 51951 : ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
4722 51951 : JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
4723 51951 : bce->flags |= TCF_IN_FOR_INIT;
4724 51951 : if (!EmitVariables(cx, bce, decl, DefineVars))
4725 0 : return false;
4726 51951 : bce->flags &= ~TCF_IN_FOR_INIT;
4727 : }
4728 :
4729 : /* Compile the object expression to the right of 'in'. */
4730 54616 : if (!EmitTree(cx, bce, forHead->pn_kid3))
4731 0 : return JS_FALSE;
4732 :
4733 : /*
4734 : * Emit a bytecode to convert top of stack value to the iterator
4735 : * object depending on the loop variant (for-in, for-each-in, or
4736 : * destructuring for-in).
4737 : */
4738 54616 : JS_ASSERT(pn->isOp(JSOP_ITER));
4739 54616 : if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
4740 0 : return false;
4741 :
4742 : /* Enter the block before the loop body, after evaluating the obj. */
4743 : StmtInfo letStmt;
4744 54616 : if (letDecl) {
4745 31380 : PushBlockScope(bce, &letStmt, *blockObj, bce->offset());
4746 31380 : letStmt.flags |= SIF_FOR_BLOCK;
4747 31380 : if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET1))
4748 0 : return false;
4749 : }
4750 :
4751 : /* Annotate so the decompiler can find the loop-closing jump. */
4752 54616 : int noteIndex = NewSrcNote(cx, bce, SRC_FOR_IN);
4753 54616 : if (noteIndex < 0)
4754 0 : return false;
4755 :
4756 : /*
4757 : * Jump down to the loop condition to minimize overhead assuming at
4758 : * least one iteration, as the other loop forms do.
4759 : */
4760 54616 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
4761 54616 : if (jmp < 0)
4762 0 : return false;
4763 :
4764 54616 : top = bce->offset();
4765 54616 : SET_STATEMENT_TOP(&stmtInfo, top);
4766 54616 : if (EmitLoopHead(cx, bce, NULL) < 0)
4767 0 : return false;
4768 :
4769 : #ifdef DEBUG
4770 54616 : int loopDepth = bce->stackDepth;
4771 : #endif
4772 :
4773 : /*
4774 : * Emit code to get the next enumeration value and assign it to the
4775 : * left hand side. The JSOP_POP after this assignment is annotated
4776 : * so that the decompiler can distinguish 'for (x in y)' from
4777 : * 'for (var x in y)'.
4778 : */
4779 54616 : if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, NULL))
4780 0 : return false;
4781 :
4782 54616 : ptrdiff_t tmp2 = bce->offset();
4783 106567 : if (forHead->pn_kid1 && NewSrcNote2(cx, bce, SRC_DECL,
4784 51951 : (forHead->pn_kid1->isOp(JSOP_DEFVAR))
4785 : ? SRC_DECL_VAR
4786 51951 : : SRC_DECL_LET) < 0) {
4787 0 : return false;
4788 : }
4789 54616 : if (Emit1(cx, bce, JSOP_POP) < 0)
4790 0 : return false;
4791 :
4792 : /* The stack should be balanced around the assignment opcode sequence. */
4793 54616 : JS_ASSERT(bce->stackDepth == loopDepth);
4794 :
4795 : /* Emit code for the loop body. */
4796 54616 : if (!EmitTree(cx, bce, forBody))
4797 0 : return false;
4798 :
4799 : /* Set loop and enclosing "update" offsets, for continue. */
4800 54616 : StmtInfo *stmt = &stmtInfo;
4801 54726 : do {
4802 54726 : stmt->update = bce->offset();
4803 : } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
4804 :
4805 : /*
4806 : * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
4807 : */
4808 54616 : SetJumpOffsetAt(bce, jmp);
4809 54616 : if (!EmitLoopEntry(cx, bce, NULL))
4810 0 : return false;
4811 54616 : if (Emit1(cx, bce, JSOP_MOREITER) < 0)
4812 0 : return false;
4813 54616 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
4814 54616 : if (beq < 0)
4815 0 : return false;
4816 :
4817 : /* Set the first srcnote offset so we can find the start of the loop body. */
4818 54616 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp2 - jmp))
4819 0 : return false;
4820 : /* Set the second srcnote offset so we can find the closing jump. */
4821 54616 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, beq - jmp))
4822 0 : return false;
4823 :
4824 : /* Fixup breaks and continues before JSOP_ITER (and JSOP_LEAVEFORINLET). */
4825 54616 : if (!PopStatementBCE(cx, bce))
4826 0 : return false;
4827 :
4828 54616 : if (letDecl) {
4829 31380 : if (!PopStatementBCE(cx, bce))
4830 0 : return false;
4831 31380 : if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
4832 0 : return false;
4833 : }
4834 :
4835 54616 : if (!NewTryNote(cx, bce, JSTRY_ITER, bce->stackDepth, top, bce->offset()))
4836 0 : return false;
4837 54616 : if (Emit1(cx, bce, JSOP_ENDITER) < 0)
4838 0 : return false;
4839 :
4840 54616 : if (letDecl) {
4841 : /* Tell the decompiler to pop but not to print. */
4842 31380 : if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
4843 0 : return false;
4844 31380 : EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
4845 : }
4846 :
4847 54616 : return true;
4848 : }
4849 :
4850 : static bool
4851 67487 : EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
4852 : {
4853 : StmtInfo stmtInfo;
4854 67487 : PushStatement(bce, &stmtInfo, STMT_FOR_LOOP, top);
4855 :
4856 67487 : ParseNode *forHead = pn->pn_left;
4857 67487 : ParseNode *forBody = pn->pn_right;
4858 :
4859 : /* C-style for (init; cond; update) ... loop. */
4860 67487 : JSOp op = JSOP_POP;
4861 67487 : ParseNode *pn3 = forHead->pn_kid1;
4862 67487 : if (!pn3) {
4863 : /* No initializer: emit an annotated nop for the decompiler. */
4864 32609 : op = JSOP_NOP;
4865 : } else {
4866 34878 : bce->flags |= TCF_IN_FOR_INIT;
4867 : #if JS_HAS_DESTRUCTURING
4868 34878 : if (pn3->isKind(PNK_ASSIGN)) {
4869 3144 : JS_ASSERT(pn3->isOp(JSOP_NOP));
4870 3144 : if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, &op))
4871 0 : return false;
4872 : }
4873 : #endif
4874 34878 : if (op == JSOP_POP) {
4875 34878 : if (!EmitTree(cx, bce, pn3))
4876 0 : return false;
4877 34878 : if (pn3->isKind(PNK_VAR) || pn3->isKind(PNK_CONST) || pn3->isKind(PNK_LET)) {
4878 : /*
4879 : * Check whether a destructuring-initialized var decl
4880 : * was optimized to a group assignment. If so, we do
4881 : * not need to emit a pop below, so switch to a nop,
4882 : * just for the decompiler.
4883 : */
4884 31653 : JS_ASSERT(pn3->isArity(PN_LIST) || pn3->isArity(PN_BINARY));
4885 31653 : if (pn3->pn_xflags & PNX_GROUPINIT)
4886 0 : op = JSOP_NOP;
4887 : }
4888 : }
4889 34878 : bce->flags &= ~TCF_IN_FOR_INIT;
4890 : }
4891 :
4892 : /*
4893 : * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
4894 : * Use tmp to hold the biased srcnote "top" offset, which differs
4895 : * from the top local variable by the length of the JSOP_GOTO
4896 : * emitted in between tmp and top if this loop has a condition.
4897 : */
4898 67487 : int noteIndex = NewSrcNote(cx, bce, SRC_FOR);
4899 67487 : if (noteIndex < 0 || Emit1(cx, bce, op) < 0)
4900 0 : return false;
4901 67487 : ptrdiff_t tmp = bce->offset();
4902 :
4903 67487 : ptrdiff_t jmp = -1;
4904 67487 : if (forHead->pn_kid2) {
4905 : /* Goto the loop condition, which branches back to iterate. */
4906 66256 : jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
4907 66256 : if (jmp < 0)
4908 0 : return false;
4909 : } else {
4910 1231 : if (op != JSOP_NOP && Emit1(cx, bce, JSOP_NOP) < 0)
4911 0 : return false;
4912 : }
4913 :
4914 67487 : top = bce->offset();
4915 67487 : SET_STATEMENT_TOP(&stmtInfo, top);
4916 :
4917 : /* Emit code for the loop body. */
4918 67487 : if (EmitLoopHead(cx, bce, forBody) < 0)
4919 0 : return false;
4920 67487 : if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody))
4921 0 : return false;
4922 67487 : if (!EmitTree(cx, bce, forBody))
4923 0 : return false;
4924 :
4925 : /* Set the second note offset so we can find the update part. */
4926 67487 : JS_ASSERT(noteIndex != -1);
4927 67487 : ptrdiff_t tmp2 = bce->offset();
4928 :
4929 : /* Set loop and enclosing "update" offsets, for continue. */
4930 67487 : StmtInfo *stmt = &stmtInfo;
4931 67507 : do {
4932 67507 : stmt->update = bce->offset();
4933 : } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
4934 :
4935 : /* Check for update code to do before the condition (if any). */
4936 67487 : pn3 = forHead->pn_kid3;
4937 67487 : if (pn3) {
4938 65453 : op = JSOP_POP;
4939 : #if JS_HAS_DESTRUCTURING
4940 65453 : if (pn3->isKind(PNK_ASSIGN)) {
4941 1152 : JS_ASSERT(pn3->isOp(JSOP_NOP));
4942 1152 : if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, &op))
4943 0 : return false;
4944 : }
4945 : #endif
4946 65453 : if (op == JSOP_POP && !EmitTree(cx, bce, pn3))
4947 0 : return false;
4948 :
4949 : /* Always emit the POP or NOP, to help the decompiler. */
4950 65453 : if (Emit1(cx, bce, op) < 0)
4951 0 : return false;
4952 :
4953 : /* Restore the absolute line number for source note readers. */
4954 65453 : ptrdiff_t lineno = pn->pn_pos.end.lineno;
4955 65453 : if (bce->currentLine() != (unsigned) lineno) {
4956 63153 : if (NewSrcNote2(cx, bce, SRC_SETLINE, lineno) < 0)
4957 0 : return false;
4958 63153 : bce->current->currentLine = (unsigned) lineno;
4959 : }
4960 : }
4961 :
4962 67487 : ptrdiff_t tmp3 = bce->offset();
4963 :
4964 67487 : if (forHead->pn_kid2) {
4965 : /* Fix up the goto from top to target the loop condition. */
4966 66256 : JS_ASSERT(jmp >= 0);
4967 66256 : SetJumpOffsetAt(bce, jmp);
4968 66256 : if (!EmitLoopEntry(cx, bce, forHead->pn_kid2))
4969 0 : return false;
4970 :
4971 66256 : if (!EmitTree(cx, bce, forHead->pn_kid2))
4972 0 : return false;
4973 : }
4974 :
4975 : /* Set the first note offset so we can find the loop condition. */
4976 67487 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp3 - tmp))
4977 0 : return false;
4978 67487 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, tmp2 - tmp))
4979 0 : return false;
4980 : /* The third note offset helps us find the loop-closing jump. */
4981 67487 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 2, bce->offset() - tmp))
4982 0 : return false;
4983 :
4984 : /* If no loop condition, just emit a loop-closing jump. */
4985 67487 : op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
4986 67487 : if (EmitJump(cx, bce, op, top - bce->offset()) < 0)
4987 0 : return false;
4988 :
4989 : /* Now fixup all breaks and continues. */
4990 67487 : return PopStatementBCE(cx, bce);
4991 : }
4992 :
4993 : static inline bool
4994 122103 : EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
4995 : {
4996 122103 : JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
4997 122103 : return pn->pn_left->isKind(PNK_FORIN)
4998 : ? EmitForIn(cx, bce, pn, top)
4999 122103 : : EmitNormalFor(cx, bce, pn, top);
5000 : }
5001 :
5002 : static bool
5003 1019370 : EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5004 : {
5005 : #if JS_HAS_XML_SUPPORT
5006 1019370 : if (pn->isArity(PN_NULLARY))
5007 36 : return Emit1(cx, bce, JSOP_GETFUNNS) >= 0;
5008 : #endif
5009 :
5010 1019334 : JSFunction *fun = pn->pn_funbox->function();
5011 1019334 : JS_ASSERT(fun->isInterpreted());
5012 1019334 : if (fun->script()) {
5013 : /*
5014 : * This second pass is needed to emit JSOP_NOP with a source note
5015 : * for the already-emitted function definition prolog opcode. See
5016 : * comments in the PNK_STATEMENTLIST case.
5017 : */
5018 40990 : JS_ASSERT(pn->isOp(JSOP_NOP));
5019 40990 : JS_ASSERT(bce->inFunction());
5020 40990 : return EmitFunctionDefNop(cx, bce, pn->pn_index);
5021 : }
5022 :
5023 68191 : JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
5024 1046535 : fun->kind() == JSFUN_INTERPRETED);
5025 :
5026 : {
5027 : /*
5028 : * Generate code for the function's body. bce2 is not allocated on the
5029 : * stack because doing so significantly reduces the maximum depth of
5030 : * nested functions we can handle. See bug 696284.
5031 : */
5032 1956688 : AutoPtr<BytecodeEmitter> bce2(cx);
5033 978344 : bce2 = cx->new_<BytecodeEmitter>(bce->parser, pn->pn_pos.begin.lineno);
5034 978344 : if (!bce2 || !bce2->init(cx))
5035 0 : return false;
5036 :
5037 978344 : bce2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION |
5038 978344 : (bce->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
5039 978344 : bce2->bindings.transfer(cx, &pn->pn_funbox->bindings);
5040 978344 : bce2->setFunction(fun);
5041 978344 : bce2->funbox = pn->pn_funbox;
5042 978344 : bce2->parent = bce;
5043 978344 : bce2->globalScope = bce->globalScope;
5044 :
5045 : /*
5046 : * js::frontend::SetStaticLevel limited static nesting depth to fit in
5047 : * 16 bits and to reserve the all-ones value, thereby reserving the
5048 : * magic FREE_UPVAR_COOKIE value. Note the bce2->staticLevel assignment
5049 : * below.
5050 : */
5051 978344 : JS_ASSERT(bce->staticLevel < JS_BITMASK(16) - 1);
5052 978344 : bce2->staticLevel = bce->staticLevel + 1;
5053 :
5054 : /* We measured the max scope depth when we parsed the function. */
5055 978344 : if (!EmitFunctionScript(cx, bce2.get(), pn->pn_body))
5056 0 : return false;
5057 : }
5058 :
5059 : /* Make the function object a literal in the outer script's pool. */
5060 978344 : unsigned index = bce->objectList.index(pn->pn_funbox);
5061 :
5062 : /* Emit a bytecode pointing to the closure object in its immediate. */
5063 978344 : if (pn->getOp() != JSOP_NOP) {
5064 751935 : if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) &&
5065 177 : NewSrcNote(cx, bce, SRC_GENEXP) < 0)
5066 : {
5067 0 : return false;
5068 : }
5069 :
5070 751758 : return EmitFunctionOp(cx, pn->getOp(), index, bce);
5071 : }
5072 :
5073 : /*
5074 : * For a script we emit the code as we parse. Thus the bytecode for
5075 : * top-level functions should go in the prolog to predefine their
5076 : * names in the variable object before the already-generated main code
5077 : * is executed. This extra work for top-level scripts is not necessary
5078 : * when we emit the code for a function. It is fully parsed prior to
5079 : * invocation of the emitter and calls to EmitTree for function
5080 : * definitions can be scheduled before generating the rest of code.
5081 : */
5082 226586 : if (!bce->inFunction()) {
5083 185596 : JS_ASSERT(!bce->topStmt);
5084 185596 : if (!BindGlobal(cx, bce, pn, fun->atom))
5085 0 : return false;
5086 185596 : if (pn->pn_cookie.isFree()) {
5087 163606 : bce->switchToProlog();
5088 163606 : MOZ_ASSERT(!fun->isFlatClosure(),
5089 163606 : "global functions can't have upvars, so they are never flat");
5090 163606 : if (!EmitFunctionOp(cx, JSOP_DEFFUN, index, bce))
5091 0 : return false;
5092 163606 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
5093 0 : return false;
5094 163606 : bce->switchToMain();
5095 : }
5096 :
5097 : /* Emit NOP for the decompiler. */
5098 185596 : if (!EmitFunctionDefNop(cx, bce, index))
5099 0 : return false;
5100 : } else {
5101 : unsigned slot;
5102 81980 : DebugOnly<BindingKind> kind = bce->bindings.lookup(cx, fun->atom, &slot);
5103 40990 : JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
5104 40990 : JS_ASSERT(index < JS_BIT(20));
5105 40990 : pn->pn_index = index;
5106 40990 : JSOp op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
5107 81203 : if (pn->isClosed() &&
5108 20165 : !bce->callsEval() &&
5109 20048 : !bce->closedVars.append(pn->pn_cookie.slot()))
5110 : {
5111 0 : return false;
5112 : }
5113 40990 : return EmitSlotObjectOp(cx, op, slot, index, bce);
5114 : }
5115 :
5116 185596 : return true;
5117 : }
5118 :
5119 : static bool
5120 383 : EmitDo(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5121 : {
5122 : /* Emit an annotated nop so we know to decompile a 'do' keyword. */
5123 383 : ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
5124 383 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
5125 0 : return false;
5126 :
5127 383 : ptrdiff_t noteIndex2 = NewSrcNote(cx, bce, SRC_WHILE);
5128 383 : if (noteIndex2 < 0)
5129 0 : return false;
5130 :
5131 : /* Compile the loop body. */
5132 383 : ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left);
5133 383 : if (top < 0)
5134 0 : return false;
5135 383 : if (!EmitLoopEntry(cx, bce, NULL))
5136 0 : return false;
5137 :
5138 : StmtInfo stmtInfo;
5139 383 : PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
5140 383 : if (!EmitTree(cx, bce, pn->pn_left))
5141 0 : return false;
5142 :
5143 : /* Set loop and enclosing label update offsets, for continue. */
5144 383 : ptrdiff_t off = bce->offset();
5145 383 : StmtInfo *stmt = &stmtInfo;
5146 383 : do {
5147 383 : stmt->update = off;
5148 : } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
5149 :
5150 : /* Compile the loop condition, now that continues know where to go. */
5151 383 : if (!EmitTree(cx, bce, pn->pn_right))
5152 0 : return false;
5153 :
5154 : /*
5155 : * Since we use JSOP_IFNE for other purposes as well as for do-while
5156 : * loops, we must store 1 + (beq - top) in the SRC_WHILE note offset,
5157 : * and the decompiler must get that delta and decompile recursively.
5158 : */
5159 383 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
5160 383 : if (beq < 0)
5161 0 : return false;
5162 :
5163 : /*
5164 : * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
5165 : * note gets bigger.
5166 : */
5167 383 : if (!SetSrcNoteOffset(cx, bce, noteIndex2, 0, beq - top))
5168 0 : return false;
5169 383 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, 1 + (off - top)))
5170 0 : return false;
5171 :
5172 383 : return PopStatementBCE(cx, bce);
5173 : }
5174 :
5175 : static bool
5176 51638 : EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5177 : {
5178 : /*
5179 : * Minimize bytecodes issued for one or more iterations by jumping to
5180 : * the condition below the body and closing the loop if the condition
5181 : * is true with a backward branch. For iteration count i:
5182 : *
5183 : * i test at the top test at the bottom
5184 : * = =============== ==================
5185 : * 0 ifeq-pass goto; ifne-fail
5186 : * 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail
5187 : * 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail
5188 : * . . .
5189 : * N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail
5190 : */
5191 : StmtInfo stmtInfo;
5192 51638 : PushStatement(bce, &stmtInfo, STMT_WHILE_LOOP, top);
5193 :
5194 51638 : ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
5195 51638 : if (noteIndex < 0)
5196 0 : return false;
5197 :
5198 51638 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
5199 51638 : if (jmp < 0)
5200 0 : return false;
5201 :
5202 51638 : top = EmitLoopHead(cx, bce, pn->pn_right);
5203 51638 : if (top < 0)
5204 0 : return false;
5205 :
5206 51638 : if (!EmitTree(cx, bce, pn->pn_right))
5207 0 : return false;
5208 :
5209 51638 : SetJumpOffsetAt(bce, jmp);
5210 51638 : if (!EmitLoopEntry(cx, bce, pn->pn_left))
5211 0 : return false;
5212 51638 : if (!EmitTree(cx, bce, pn->pn_left))
5213 0 : return false;
5214 :
5215 51638 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
5216 51638 : if (beq < 0)
5217 0 : return false;
5218 :
5219 51638 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
5220 0 : return false;
5221 :
5222 51638 : return PopStatementBCE(cx, bce);
5223 : }
5224 :
5225 : static bool
5226 53010 : EmitBreak(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
5227 : {
5228 53010 : StmtInfo *stmt = bce->topStmt;
5229 : SrcNoteType noteType;
5230 : jsatomid labelIndex;
5231 53010 : if (label) {
5232 90 : if (!bce->makeAtomIndex(label, &labelIndex))
5233 0 : return false;
5234 :
5235 594 : while (stmt->type != STMT_LABEL || stmt->label != label)
5236 414 : stmt = stmt->down;
5237 90 : noteType = SRC_BREAK2LABEL;
5238 : } else {
5239 52920 : labelIndex = INVALID_ATOMID;
5240 181383 : while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
5241 75543 : stmt = stmt->down;
5242 52920 : noteType = (stmt->type == STMT_SWITCH) ? SRC_SWITCHBREAK : SRC_BREAK;
5243 : }
5244 :
5245 53010 : return EmitGoto(cx, bce, stmt, &stmt->breaks, labelIndex, noteType) >= 0;
5246 : }
5247 :
5248 : static bool
5249 38621 : EmitContinue(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
5250 : {
5251 38621 : StmtInfo *stmt = bce->topStmt;
5252 : SrcNoteType noteType;
5253 : jsatomid labelIndex;
5254 38621 : if (label) {
5255 46 : if (!bce->makeAtomIndex(label, &labelIndex))
5256 0 : return false;
5257 :
5258 : /* Find the loop statement enclosed by the matching label. */
5259 46 : StmtInfo *loop = NULL;
5260 301 : while (stmt->type != STMT_LABEL || stmt->label != label) {
5261 209 : if (STMT_IS_LOOP(stmt))
5262 77 : loop = stmt;
5263 209 : stmt = stmt->down;
5264 : }
5265 46 : stmt = loop;
5266 46 : noteType = SRC_CONT2LABEL;
5267 : } else {
5268 38575 : labelIndex = INVALID_ATOMID;
5269 236227 : while (!STMT_IS_LOOP(stmt))
5270 159077 : stmt = stmt->down;
5271 38575 : noteType = SRC_CONTINUE;
5272 : }
5273 :
5274 38621 : return EmitGoto(cx, bce, stmt, &stmt->continues, labelIndex, noteType) >= 0;
5275 : }
5276 :
5277 : static bool
5278 660086 : EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5279 : {
5280 : /* Push a return value */
5281 660086 : if (ParseNode *pn2 = pn->pn_kid) {
5282 548038 : if (!EmitTree(cx, bce, pn2))
5283 0 : return false;
5284 : } else {
5285 : /* No explicit return value provided */
5286 112048 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
5287 0 : return false;
5288 : }
5289 :
5290 : /*
5291 : * EmitNonLocalJumpFixup may add fixup bytecode to close open try
5292 : * blocks having finally clauses and to exit intermingled let blocks.
5293 : * We can't simply transfer control flow to our caller in that case,
5294 : * because we must gosub to those finally clauses from inner to outer,
5295 : * with the correct stack pointer (i.e., after popping any with,
5296 : * for/in, etc., slots nested inside the finally's try).
5297 : *
5298 : * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
5299 : * extra JSOP_RETRVAL after the fixups.
5300 : */
5301 660086 : ptrdiff_t top = bce->offset();
5302 :
5303 660086 : if (Emit1(cx, bce, JSOP_RETURN) < 0)
5304 0 : return false;
5305 660086 : if (!EmitNonLocalJumpFixup(cx, bce, NULL))
5306 0 : return false;
5307 660086 : if (top + JSOP_RETURN_LENGTH != bce->offset()) {
5308 73810 : bce->base()[top] = JSOP_SETRVAL;
5309 73810 : if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
5310 0 : return false;
5311 : }
5312 :
5313 660086 : return true;
5314 : }
5315 :
5316 : static bool
5317 2400798 : EmitStatementList(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5318 : {
5319 2400798 : JS_ASSERT(pn->isArity(PN_LIST));
5320 :
5321 2400798 : ptrdiff_t noteIndex = -1;
5322 2400798 : ptrdiff_t tmp = bce->offset();
5323 2400798 : if (pn->pn_xflags & PNX_NEEDBRACES) {
5324 102 : noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0);
5325 102 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
5326 0 : return false;
5327 : }
5328 :
5329 : StmtInfo stmtInfo;
5330 2400798 : PushStatement(bce, &stmtInfo, STMT_BLOCK, top);
5331 :
5332 2400798 : ParseNode *pnchild = pn->pn_head;
5333 2400798 : if (pn->pn_xflags & PNX_FUNCDEFS) {
5334 : /*
5335 : * This block contains top-level function definitions. To ensure
5336 : * that we emit the bytecode defining them before the rest of code
5337 : * in the block we use a separate pass over functions. During the
5338 : * main pass later the emitter will add JSOP_NOP with source notes
5339 : * for the function to preserve the original functions position
5340 : * when decompiling.
5341 : *
5342 : * Currently this is used only for functions, as compile-as-we go
5343 : * mode for scripts does not allow separate emitter passes.
5344 : */
5345 27995 : JS_ASSERT(bce->inFunction());
5346 27995 : if (pn->pn_xflags & PNX_DESTRUCT) {
5347 : /*
5348 : * Assign the destructuring arguments before defining any
5349 : * functions, see bug 419662.
5350 : */
5351 4 : JS_ASSERT(pnchild->isKind(PNK_SEMI));
5352 4 : JS_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_CONST));
5353 4 : if (!EmitTree(cx, bce, pnchild))
5354 0 : return false;
5355 4 : pnchild = pnchild->pn_next;
5356 : }
5357 :
5358 308437 : for (ParseNode *pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
5359 280442 : if (pn2->isKind(PNK_FUNCTION)) {
5360 40990 : if (pn2->isOp(JSOP_NOP)) {
5361 40990 : if (!EmitTree(cx, bce, pn2))
5362 0 : return false;
5363 : } else {
5364 : /*
5365 : * JSOP_DEFFUN in a top-level block with function
5366 : * definitions appears, for example, when "if (true)"
5367 : * is optimized away from "if (true) function x() {}".
5368 : * See bug 428424.
5369 : */
5370 0 : JS_ASSERT(pn2->isOp(JSOP_DEFFUN));
5371 : }
5372 : }
5373 : }
5374 : }
5375 :
5376 11230146 : for (ParseNode *pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
5377 8829348 : if (!EmitTree(cx, bce, pn2))
5378 0 : return false;
5379 : }
5380 :
5381 2400798 : if (noteIndex >= 0 && !SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, bce->offset() - tmp))
5382 0 : return false;
5383 :
5384 2400798 : return PopStatementBCE(cx, bce);
5385 : }
5386 :
5387 : static bool
5388 6474702 : EmitStatement(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5389 : {
5390 6474702 : JS_ASSERT(pn->isKind(PNK_SEMI));
5391 :
5392 6474702 : ParseNode *pn2 = pn->pn_kid;
5393 6474702 : if (!pn2)
5394 4830 : return true;
5395 :
5396 : /*
5397 : * Top-level or called-from-a-native JS_Execute/EvaluateScript,
5398 : * debugger, and eval frames may need the value of the ultimate
5399 : * expression statement as the script's result, despite the fact
5400 : * that it appears useless to the compiler.
5401 : *
5402 : * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
5403 : * calling JS_Compile* to suppress JSOP_POPV.
5404 : */
5405 : bool wantval;
5406 6469872 : JSBool useful = wantval = !(bce->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
5407 :
5408 : /* Don't eliminate expressions with side effects. */
5409 6469872 : if (!useful) {
5410 5774353 : if (!CheckSideEffects(cx, bce, pn2, &useful))
5411 0 : return false;
5412 :
5413 : /*
5414 : * Don't eliminate apparently useless expressions if they are
5415 : * labeled expression statements. The tc->topStmt->update test
5416 : * catches the case where we are nesting in EmitTree for a labeled
5417 : * compound statement.
5418 : */
5419 5774380 : if (bce->topStmt &&
5420 : bce->topStmt->type == STMT_LABEL &&
5421 27 : bce->topStmt->update >= bce->offset())
5422 : {
5423 27 : useful = true;
5424 : }
5425 : }
5426 :
5427 6469872 : if (useful) {
5428 6466410 : JSOp op = wantval ? JSOP_POPV : JSOP_POP;
5429 6466410 : JS_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP));
5430 : #if JS_HAS_DESTRUCTURING
5431 14531945 : if (!wantval &&
5432 5770891 : pn2->isKind(PNK_ASSIGN) &&
5433 2294644 : !MaybeEmitGroupAssignment(cx, bce, op, pn2, &op))
5434 : {
5435 0 : return false;
5436 : }
5437 : #endif
5438 6466410 : if (op != JSOP_NOP) {
5439 6466089 : if (!EmitTree(cx, bce, pn2))
5440 0 : return false;
5441 6466089 : if (Emit1(cx, bce, op) < 0)
5442 0 : return false;
5443 : }
5444 3462 : } else if (!pn->isDirectivePrologueMember()) {
5445 : /* Don't complain about directive prologue members; just don't emit their code. */
5446 704 : bce->current->currentLine = pn2->pn_pos.begin.lineno;
5447 704 : if (!ReportCompileErrorNumber(cx, bce->tokenStream(), pn2,
5448 704 : JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_USELESS_EXPR))
5449 : {
5450 0 : return false;
5451 : }
5452 : }
5453 :
5454 6469872 : return true;
5455 : }
5456 :
5457 : static bool
5458 37494 : EmitDelete(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5459 : {
5460 : /*
5461 : * Under ECMA 3, deleting a non-reference returns true -- but alas we
5462 : * must evaluate the operand if it appears it might have side effects.
5463 : */
5464 37494 : ParseNode *pn2 = pn->pn_kid;
5465 37494 : switch (pn2->getKind()) {
5466 : case PNK_NAME:
5467 : {
5468 491 : if (!BindNameToSlot(cx, bce, pn2))
5469 0 : return false;
5470 491 : JSOp op = pn2->getOp();
5471 491 : if (op == JSOP_FALSE) {
5472 18 : if (Emit1(cx, bce, op) < 0)
5473 0 : return false;
5474 : } else {
5475 473 : if (!EmitAtomOp(cx, pn2, op, bce))
5476 0 : return false;
5477 : }
5478 491 : break;
5479 : }
5480 : case PNK_DOT:
5481 22915 : if (!EmitPropOp(cx, pn2, JSOP_DELPROP, bce, false))
5482 0 : return false;
5483 22915 : break;
5484 : #if JS_HAS_XML_SUPPORT
5485 : case PNK_DBLDOT:
5486 0 : JS_ASSERT(!bce->inStrictMode());
5487 0 : if (!EmitElemOp(cx, pn2, JSOP_DELDESC, bce))
5488 0 : return false;
5489 0 : break;
5490 : #endif
5491 : case PNK_LB:
5492 14088 : if (!EmitElemOp(cx, pn2, JSOP_DELELEM, bce))
5493 0 : return false;
5494 14088 : break;
5495 : default:
5496 : {
5497 : /*
5498 : * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
5499 : * to foo(), true (a comma expression, requiring SRC_PCDELTA).
5500 : */
5501 0 : JSBool useful = false;
5502 0 : if (!CheckSideEffects(cx, bce, pn2, &useful))
5503 0 : return false;
5504 :
5505 : ptrdiff_t off, noteIndex;
5506 0 : if (useful) {
5507 0 : JS_ASSERT_IF(pn2->isKind(PNK_LP), !(pn2->pn_xflags & PNX_SETCALL));
5508 0 : if (!EmitTree(cx, bce, pn2))
5509 0 : return false;
5510 0 : off = bce->offset();
5511 0 : noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
5512 0 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_POP) < 0)
5513 0 : return false;
5514 : } else {
5515 0 : off = noteIndex = -1;
5516 : }
5517 :
5518 0 : if (Emit1(cx, bce, JSOP_TRUE) < 0)
5519 0 : return false;
5520 0 : if (noteIndex >= 0) {
5521 0 : ptrdiff_t tmp = bce->offset();
5522 0 : if (!SetSrcNoteOffset(cx, bce, unsigned(noteIndex), 0, tmp - off))
5523 0 : return false;
5524 : }
5525 : }
5526 : }
5527 :
5528 37494 : return true;
5529 : }
5530 :
5531 : static bool
5532 3331826 : EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5533 : {
5534 3331826 : bool callop = pn->isKind(PNK_LP);
5535 :
5536 : /*
5537 : * Emit callable invocation or operator new (constructor call) code.
5538 : * First, emit code for the left operand to evaluate the callable or
5539 : * constructable object expression.
5540 : *
5541 : * For operator new applied to other expressions than E4X ones, we emit
5542 : * JSOP_GETPROP instead of JSOP_CALLPROP, etc. This is necessary to
5543 : * interpose the lambda-initialized method read barrier -- see the code
5544 : * in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP.
5545 : *
5546 : * Then (or in a call case that has no explicit reference-base
5547 : * object) we emit JSOP_UNDEFINED to produce the undefined |this|
5548 : * value required for calls (which non-strict mode functions
5549 : * will box into the global object).
5550 : */
5551 3331826 : ParseNode *pn2 = pn->pn_head;
5552 3331826 : switch (pn2->getKind()) {
5553 : case PNK_NAME:
5554 1003934 : if (!EmitNameOp(cx, bce, pn2, callop))
5555 0 : return false;
5556 1003934 : break;
5557 : case PNK_DOT:
5558 2313918 : if (!EmitPropOp(cx, pn2, pn2->getOp(), bce, callop))
5559 0 : return false;
5560 2313918 : break;
5561 : case PNK_LB:
5562 4740 : JS_ASSERT(pn2->isOp(JSOP_GETELEM));
5563 4740 : if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, bce))
5564 0 : return false;
5565 4740 : break;
5566 : #if JS_HAS_XML_SUPPORT
5567 : case PNK_XMLUNARY:
5568 9 : JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
5569 9 : if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
5570 0 : return false;
5571 9 : callop = true; /* suppress JSOP_UNDEFINED after */
5572 9 : break;
5573 : #endif
5574 : default:
5575 9225 : if (!EmitTree(cx, bce, pn2))
5576 0 : return false;
5577 9225 : callop = false; /* trigger JSOP_UNDEFINED after */
5578 9225 : break;
5579 : }
5580 3331826 : if (!callop && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
5581 0 : return false;
5582 :
5583 : /* Remember start of callable-object bytecode for decompilation hint. */
5584 3331826 : ptrdiff_t off = top;
5585 :
5586 : /*
5587 : * Emit code for each argument in order, then emit the JSOP_*CALL or
5588 : * JSOP_NEW bytecode with a two-byte immediate telling how many args
5589 : * were pushed on the operand stack.
5590 : */
5591 3331826 : unsigned oldflags = bce->flags;
5592 3331826 : bce->flags &= ~TCF_IN_FOR_INIT;
5593 7695471 : for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
5594 4363645 : if (!EmitTree(cx, bce, pn3))
5595 0 : return false;
5596 : }
5597 3331826 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
5598 3331826 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - off) < 0)
5599 0 : return false;
5600 :
5601 3331826 : uint32_t argc = pn->pn_count - 1;
5602 3331826 : if (Emit3(cx, bce, pn->getOp(), ARGC_HI(argc), ARGC_LO(argc)) < 0)
5603 0 : return false;
5604 3331826 : CheckTypeSet(cx, bce, pn->getOp());
5605 3331826 : if (pn->isOp(JSOP_EVAL))
5606 4789 : EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno);
5607 3331826 : if (pn->pn_xflags & PNX_SETCALL) {
5608 27 : if (Emit1(cx, bce, JSOP_SETCALL) < 0)
5609 0 : return false;
5610 : }
5611 3331826 : return true;
5612 : }
5613 :
5614 : static bool
5615 229653 : EmitLogical(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5616 : {
5617 : /*
5618 : * JSOP_OR converts the operand on the stack to boolean, leaves the original
5619 : * value on the stack and jumps if true; otherwise it falls into the next
5620 : * bytecode, which pops the left operand and then evaluates the right operand.
5621 : * The jump goes around the right operand evaluation.
5622 : *
5623 : * JSOP_AND converts the operand on the stack to boolean and jumps if false;
5624 : * otherwise it falls into the right operand's bytecode.
5625 : */
5626 :
5627 229653 : if (pn->isArity(PN_BINARY)) {
5628 197108 : if (!EmitTree(cx, bce, pn->pn_left))
5629 0 : return false;
5630 197108 : ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
5631 197108 : if (top < 0)
5632 0 : return false;
5633 197108 : if (Emit1(cx, bce, JSOP_POP) < 0)
5634 0 : return false;
5635 197108 : if (!EmitTree(cx, bce, pn->pn_right))
5636 0 : return false;
5637 197108 : ptrdiff_t off = bce->offset();
5638 197108 : jsbytecode *pc = bce->code(top);
5639 197108 : SET_JUMP_OFFSET(pc, off - top);
5640 197108 : *pc = pn->getOp();
5641 197108 : return true;
5642 : }
5643 :
5644 32545 : JS_ASSERT(pn->isArity(PN_LIST));
5645 32545 : JS_ASSERT(pn->pn_head->pn_next->pn_next);
5646 :
5647 : /* Left-associative operator chain: avoid too much recursion. */
5648 32545 : ParseNode *pn2 = pn->pn_head;
5649 32545 : if (!EmitTree(cx, bce, pn2))
5650 0 : return false;
5651 32545 : ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
5652 32545 : if (top < 0)
5653 0 : return false;
5654 32545 : if (Emit1(cx, bce, JSOP_POP) < 0)
5655 0 : return false;
5656 :
5657 : /* Emit nodes between the head and the tail. */
5658 32545 : ptrdiff_t jmp = top;
5659 108234 : while ((pn2 = pn2->pn_next)->pn_next) {
5660 43144 : if (!EmitTree(cx, bce, pn2))
5661 0 : return false;
5662 43144 : ptrdiff_t off = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
5663 43144 : if (off < 0)
5664 0 : return false;
5665 43144 : if (Emit1(cx, bce, JSOP_POP) < 0)
5666 0 : return false;
5667 43144 : SET_JUMP_OFFSET(bce->code(jmp), off - jmp);
5668 43144 : jmp = off;
5669 : }
5670 32545 : if (!EmitTree(cx, bce, pn2))
5671 0 : return false;
5672 :
5673 32545 : pn2 = pn->pn_head;
5674 32545 : ptrdiff_t off = bce->offset();
5675 75689 : do {
5676 75689 : jsbytecode *pc = bce->code(top);
5677 75689 : ptrdiff_t tmp = GET_JUMP_OFFSET(pc);
5678 75689 : SET_JUMP_OFFSET(pc, off - top);
5679 75689 : *pc = pn->getOp();
5680 75689 : top += tmp;
5681 : } while ((pn2 = pn2->pn_next)->pn_next);
5682 :
5683 32545 : return true;
5684 : }
5685 :
5686 : static bool
5687 1737187 : EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5688 : {
5689 : /* Emit lvalue-specialized code for ++/-- operators. */
5690 1737187 : ParseNode *pn2 = pn->pn_kid;
5691 1737187 : JS_ASSERT(!pn2->isKind(PNK_RP));
5692 1737187 : JSOp op = pn->getOp();
5693 1737187 : switch (pn2->getKind()) {
5694 : case PNK_DOT:
5695 4874 : if (!EmitPropIncDec(cx, pn2, op, bce))
5696 0 : return false;
5697 4874 : break;
5698 : case PNK_LB:
5699 1638851 : if (!EmitElemIncDec(cx, pn2, op, bce))
5700 0 : return false;
5701 1638851 : break;
5702 : case PNK_LP:
5703 9 : if (!EmitTree(cx, bce, pn2))
5704 0 : return false;
5705 9 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
5706 0 : return false;
5707 9 : if (Emit1(cx, bce, op) < 0)
5708 0 : return false;
5709 : /*
5710 : * This is dead code for the decompiler, don't generate
5711 : * a decomposed version of the opcode. We do need to balance
5712 : * the stacks in the decomposed version.
5713 : */
5714 9 : JS_ASSERT(js_CodeSpec[op].format & JOF_DECOMPOSE);
5715 9 : JS_ASSERT(js_CodeSpec[op].format & JOF_ELEM);
5716 9 : if (Emit1(cx, bce, (JSOp)1) < 0)
5717 0 : return false;
5718 9 : if (Emit1(cx, bce, JSOP_POP) < 0)
5719 0 : return false;
5720 9 : break;
5721 : #if JS_HAS_XML_SUPPORT
5722 : case PNK_XMLUNARY:
5723 9 : JS_ASSERT(!bce->inStrictMode());
5724 9 : JS_ASSERT(pn2->isOp(JSOP_SETXMLNAME));
5725 9 : if (!EmitTree(cx, bce, pn2->pn_kid))
5726 0 : return false;
5727 9 : if (Emit1(cx, bce, JSOP_BINDXMLNAME) < 0)
5728 0 : return false;
5729 9 : if (!EmitElemIncDec(cx, NULL, op, bce))
5730 0 : return false;
5731 9 : break;
5732 : #endif
5733 : default:
5734 93444 : JS_ASSERT(pn2->isKind(PNK_NAME));
5735 93444 : pn2->setOp(op);
5736 93444 : if (!BindNameToSlot(cx, bce, pn2))
5737 0 : return false;
5738 93444 : op = pn2->getOp();
5739 93444 : if (op == JSOP_CALLEE) {
5740 0 : if (Emit1(cx, bce, op) < 0)
5741 0 : return false;
5742 93444 : } else if (!pn2->pn_cookie.isFree()) {
5743 70680 : jsatomid atomIndex = pn2->pn_cookie.asInteger();
5744 70680 : EMIT_UINT16_IMM_OP(op, atomIndex);
5745 : } else {
5746 22764 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
5747 22764 : if (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)) {
5748 22755 : if (!EmitNameIncDec(cx, pn2, op, bce))
5749 0 : return false;
5750 : } else {
5751 9 : if (!EmitAtomOp(cx, pn2, op, bce))
5752 0 : return false;
5753 : }
5754 22764 : break;
5755 : }
5756 70680 : if (pn2->isConst()) {
5757 0 : if (Emit1(cx, bce, JSOP_POS) < 0)
5758 0 : return false;
5759 0 : op = pn->getOp();
5760 0 : if (!(js_CodeSpec[op].format & JOF_POST)) {
5761 0 : if (Emit1(cx, bce, JSOP_ONE) < 0)
5762 0 : return false;
5763 0 : op = (js_CodeSpec[op].format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
5764 0 : if (Emit1(cx, bce, op) < 0)
5765 0 : return false;
5766 : }
5767 : }
5768 : }
5769 1737187 : return true;
5770 : }
5771 :
5772 : /*
5773 : * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127. See
5774 : * the comment on EmitSwitch.
5775 : */
5776 : MOZ_NEVER_INLINE static bool
5777 193 : EmitLabel(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5778 : {
5779 : /*
5780 : * Emit a JSOP_LABEL instruction. The argument is the offset to the statement
5781 : * following the labeled statement. This op has either a SRC_LABEL or
5782 : * SRC_LABELBRACE source note for the decompiler.
5783 : */
5784 193 : JSAtom *atom = pn->pn_atom;
5785 :
5786 : jsatomid index;
5787 193 : if (!bce->makeAtomIndex(atom, &index))
5788 0 : return false;
5789 :
5790 193 : ParseNode *pn2 = pn->expr();
5791 193 : SrcNoteType noteType = (pn2->isKind(PNK_STATEMENTLIST) ||
5792 193 : (pn2->isKind(PNK_LEXICALSCOPE) &&
5793 0 : pn2->expr()->isKind(PNK_STATEMENTLIST)))
5794 : ? SRC_LABELBRACE
5795 386 : : SRC_LABEL;
5796 193 : ptrdiff_t noteIndex = NewSrcNote2(cx, bce, noteType, ptrdiff_t(index));
5797 193 : if (noteIndex < 0)
5798 0 : return false;
5799 :
5800 193 : ptrdiff_t top = EmitJump(cx, bce, JSOP_LABEL, 0);
5801 193 : if (top < 0)
5802 0 : return false;
5803 :
5804 : /* Emit code for the labeled statement. */
5805 : StmtInfo stmtInfo;
5806 193 : PushStatement(bce, &stmtInfo, STMT_LABEL, bce->offset());
5807 193 : stmtInfo.label = atom;
5808 193 : if (!EmitTree(cx, bce, pn2))
5809 0 : return false;
5810 193 : if (!PopStatementBCE(cx, bce))
5811 0 : return false;
5812 :
5813 : /* Patch the JSOP_LABEL offset. */
5814 193 : SetJumpOffsetAt(bce, top);
5815 :
5816 : /* If the statement was compound, emit a note for the end brace. */
5817 193 : if (noteType == SRC_LABELBRACE) {
5818 0 : if (NewSrcNote(cx, bce, SRC_ENDBRACE) < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
5819 0 : return false;
5820 : }
5821 :
5822 193 : return true;
5823 : }
5824 :
5825 : static bool
5826 934 : EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5827 : {
5828 934 : JS_ASSERT(pn->isArity(PN_LIST));
5829 : StmtInfo stmtInfo;
5830 934 : PushStatement(bce, &stmtInfo, STMT_SEQ, top);
5831 2802 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5832 1868 : if (!EmitTree(cx, bce, pn2))
5833 0 : return false;
5834 : }
5835 934 : return PopStatementBCE(cx, bce);
5836 : }
5837 :
5838 : static bool
5839 68371 : EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
5840 : {
5841 : /* Emit the condition, then branch if false to the else part. */
5842 68371 : if (!EmitTree(cx, bce, &conditional.condition()))
5843 0 : return false;
5844 68371 : ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
5845 68371 : if (noteIndex < 0)
5846 0 : return false;
5847 68371 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
5848 68371 : if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression()))
5849 0 : return false;
5850 :
5851 : /* Jump around else, fixup the branch, emit else, fixup jump. */
5852 68371 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
5853 68371 : if (jmp < 0)
5854 0 : return false;
5855 68371 : SetJumpOffsetAt(bce, beq);
5856 :
5857 : /*
5858 : * Because each branch pushes a single value, but our stack budgeting
5859 : * analysis ignores branches, we now have to adjust bce->stackDepth to
5860 : * ignore the value pushed by the first branch. Execution will follow
5861 : * only one path, so we must decrement bce->stackDepth.
5862 : *
5863 : * Failing to do this will foil code, such as let expression and block
5864 : * code generation, which must use the stack depth to compute local
5865 : * stack indexes correctly.
5866 : */
5867 68371 : JS_ASSERT(bce->stackDepth > 0);
5868 68371 : bce->stackDepth--;
5869 68371 : if (!EmitTree(cx, bce, &conditional.elseExpression()))
5870 0 : return false;
5871 68371 : SetJumpOffsetAt(bce, jmp);
5872 68371 : return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
5873 : }
5874 :
5875 : static bool
5876 216320 : EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5877 : {
5878 : #if JS_HAS_DESTRUCTURING_SHORTHAND
5879 216320 : if (pn->pn_xflags & PNX_DESTRUCT) {
5880 : ReportCompileErrorNumber(cx, bce->tokenStream(), pn, JSREPORT_ERROR,
5881 0 : JSMSG_BAD_OBJECT_INIT);
5882 0 : return false;
5883 : }
5884 : #endif
5885 :
5886 216320 : if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
5887 1842 : return EmitSingletonInitialiser(cx, bce, pn);
5888 :
5889 : /*
5890 : * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
5891 : * a new object and in source order evaluating each property value and
5892 : * adding the property to the object, without invoking latent setters.
5893 : * We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to
5894 : * ignore setters and to avoid dup'ing and popping the object as each
5895 : * property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
5896 : */
5897 214478 : ptrdiff_t offset = bce->next() - bce->base();
5898 214478 : if (!EmitNewInit(cx, bce, JSProto_Object, pn))
5899 0 : return false;
5900 :
5901 : /*
5902 : * Try to construct the shape of the object as we go, so we can emit a
5903 : * JSOP_NEWOBJECT with the final shape instead.
5904 : */
5905 214478 : JSObject *obj = NULL;
5906 214478 : if (bce->compileAndGo()) {
5907 9811 : gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
5908 9811 : obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
5909 9811 : if (!obj)
5910 0 : return false;
5911 : }
5912 :
5913 214478 : unsigned methodInits = 0, slowMethodInits = 0;
5914 1082713 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5915 : /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
5916 868235 : ParseNode *pn3 = pn2->pn_left;
5917 868235 : if (pn3->isKind(PNK_NUMBER)) {
5918 4205 : if (!EmitNumberOp(cx, pn3->pn_dval, bce))
5919 0 : return false;
5920 : }
5921 :
5922 : /* Emit code for the property initializer. */
5923 868235 : if (!EmitTree(cx, bce, pn2->pn_right))
5924 0 : return false;
5925 :
5926 868235 : JSOp op = pn2->getOp();
5927 868235 : if (op == JSOP_GETTER || op == JSOP_SETTER) {
5928 67597 : obj = NULL;
5929 67597 : if (Emit1(cx, bce, op) < 0)
5930 0 : return false;
5931 : }
5932 :
5933 : /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
5934 868235 : if (pn3->isKind(PNK_NUMBER)) {
5935 4205 : obj = NULL;
5936 4205 : if (NewSrcNote(cx, bce, SRC_INITPROP) < 0)
5937 0 : return false;
5938 4205 : if (Emit1(cx, bce, JSOP_INITELEM) < 0)
5939 0 : return false;
5940 : } else {
5941 864030 : JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
5942 : jsatomid index;
5943 864030 : if (!bce->makeAtomIndex(pn3->pn_atom, &index))
5944 0 : return false;
5945 :
5946 : /*
5947 : * Disable NEWOBJECT on initializers that set __proto__, which has
5948 : * a non-standard setter on objects.
5949 : */
5950 864030 : if (pn3->pn_atom == cx->runtime->atomState.protoAtom)
5951 9780 : obj = NULL;
5952 864030 : op = JSOP_INITPROP;
5953 :
5954 864030 : if (obj) {
5955 10518 : JS_ASSERT(!obj->inDictionaryMode());
5956 21036 : if (!DefineNativeProperty(cx, obj, ATOM_TO_JSID(pn3->pn_atom),
5957 : UndefinedValue(), NULL, NULL,
5958 21036 : JSPROP_ENUMERATE, 0, 0))
5959 : {
5960 0 : return false;
5961 : }
5962 10518 : if (obj->inDictionaryMode())
5963 0 : obj = NULL;
5964 : }
5965 :
5966 864030 : if (!EmitIndex32(cx, op, index, bce))
5967 0 : return false;
5968 : }
5969 : }
5970 :
5971 214478 : if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
5972 0 : return false;
5973 :
5974 214478 : if (obj) {
5975 : /*
5976 : * The object survived and has a predictable shape: update the original
5977 : * bytecode.
5978 : */
5979 9143 : ObjectBox *objbox = bce->parser->newObjectBox(obj);
5980 9143 : if (!objbox)
5981 0 : return false;
5982 9143 : unsigned index = bce->objectList.index(objbox);
5983 : MOZ_STATIC_ASSERT(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
5984 : "newinit and newobject must have equal length to edit in-place");
5985 9143 : EMIT_UINT32_IN_PLACE(offset, JSOP_NEWOBJECT, uint32_t(index));
5986 : }
5987 :
5988 214478 : return true;
5989 : }
5990 :
5991 : static bool
5992 224358 : EmitArray(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5993 : {
5994 : /*
5995 : * Emit code for [a, b, c] that is equivalent to constructing a new
5996 : * array and in source order evaluating each element value and adding
5997 : * it to the array, without invoking latent setters. We use the
5998 : * JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to
5999 : * avoid dup'ing and popping the array as each element is added, as
6000 : * JSOP_SETELEM/JSOP_SETPROP would do.
6001 : */
6002 :
6003 : #if JS_HAS_GENERATORS
6004 224358 : if (pn->isKind(PNK_ARRAYCOMP)) {
6005 12678 : if (!EmitNewInit(cx, bce, JSProto_Array, pn))
6006 0 : return false;
6007 :
6008 : /*
6009 : * Pass the new array's stack index to the PNK_ARRAYPUSH case via
6010 : * bce->arrayCompDepth, then simply traverse the PNK_FOR node and
6011 : * its kids under pn2 to generate this comprehension.
6012 : */
6013 12678 : JS_ASSERT(bce->stackDepth > 0);
6014 12678 : unsigned saveDepth = bce->arrayCompDepth;
6015 12678 : bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
6016 12678 : if (!EmitTree(cx, bce, pn->pn_head))
6017 0 : return false;
6018 12678 : bce->arrayCompDepth = saveDepth;
6019 :
6020 : /* Emit the usual op needed for decompilation. */
6021 12678 : return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
6022 : }
6023 : #endif /* JS_HAS_GENERATORS */
6024 :
6025 211680 : if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
6026 5434 : return EmitSingletonInitialiser(cx, bce, pn);
6027 :
6028 206246 : ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3);
6029 206246 : if (off < 0)
6030 0 : return false;
6031 206246 : CheckTypeSet(cx, bce, JSOP_NEWARRAY);
6032 206246 : jsbytecode *pc = bce->code(off);
6033 206246 : SET_UINT24(pc, pn->pn_count);
6034 :
6035 206246 : ParseNode *pn2 = pn->pn_head;
6036 : jsatomid atomIndex;
6037 5494817 : for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
6038 5288571 : if (!EmitNumberOp(cx, atomIndex, bce))
6039 0 : return false;
6040 5288571 : if (pn2->isKind(PNK_COMMA) && pn2->isArity(PN_NULLARY)) {
6041 4899 : if (Emit1(cx, bce, JSOP_HOLE) < 0)
6042 0 : return false;
6043 : } else {
6044 5283672 : if (!EmitTree(cx, bce, pn2))
6045 0 : return false;
6046 : }
6047 5288571 : if (Emit1(cx, bce, JSOP_INITELEM) < 0)
6048 0 : return false;
6049 : }
6050 206246 : JS_ASSERT(atomIndex == pn->pn_count);
6051 :
6052 206246 : if (pn->pn_xflags & PNX_ENDCOMMA) {
6053 : /* Emit a source note so we know to decompile an extra comma. */
6054 2337 : if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
6055 0 : return false;
6056 : }
6057 :
6058 : /* Emit an op to finish the array and aid in decompilation. */
6059 206246 : return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
6060 : }
6061 :
6062 : static bool
6063 531783 : EmitUnary(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
6064 : {
6065 : /* Unary op, including unary +/-. */
6066 531783 : JSOp op = pn->getOp();
6067 531783 : ParseNode *pn2 = pn->pn_kid;
6068 :
6069 531783 : JS_ASSERT(op != JSOP_XMLNAME);
6070 531783 : if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
6071 0 : op = JSOP_TYPEOFEXPR;
6072 :
6073 531783 : unsigned oldflags = bce->flags;
6074 531783 : bce->flags &= ~TCF_IN_FOR_INIT;
6075 531783 : if (!EmitTree(cx, bce, pn2))
6076 0 : return false;
6077 :
6078 531783 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
6079 531783 : return Emit1(cx, bce, op) >= 0;
6080 : }
6081 :
6082 : JSBool
6083 59282303 : frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
6084 : {
6085 59282303 : JS_CHECK_RECURSION(cx, return JS_FALSE);
6086 :
6087 118564606 : EmitLevelManager elm(bce);
6088 :
6089 59282303 : JSBool ok = true;
6090 59282303 : ptrdiff_t top = bce->offset();
6091 59282303 : pn->pn_offset = top;
6092 :
6093 : /* Emit notes to tell the current bytecode's source line number. */
6094 59282303 : UPDATE_LINE_NUMBER_NOTES(cx, bce, pn->pn_pos.begin.lineno);
6095 :
6096 59282303 : switch (pn->getKind()) {
6097 : case PNK_FUNCTION:
6098 1019370 : ok = EmitFunc(cx, bce, pn);
6099 1019370 : break;
6100 :
6101 : case PNK_ARGSBODY:
6102 : {
6103 646026 : ParseNode *pnlast = pn->last();
6104 1852199 : for (ParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
6105 1206173 : if (!pn2->isDefn())
6106 18 : continue;
6107 1206155 : if (!BindNameToSlot(cx, bce, pn2))
6108 0 : return JS_FALSE;
6109 1206155 : if (JOF_OPTYPE(pn2->getOp()) == JOF_QARG && bce->shouldNoteClosedName(pn2)) {
6110 113998 : if (!bce->closedArgs.append(pn2->pn_cookie.slot()))
6111 0 : return JS_FALSE;
6112 : }
6113 : }
6114 646026 : ok = EmitTree(cx, bce, pnlast);
6115 646026 : break;
6116 : }
6117 :
6118 : case PNK_UPVARS:
6119 811155 : JS_ASSERT(pn->pn_names->count() != 0);
6120 811155 : bce->roLexdeps = pn->pn_names;
6121 811155 : ok = EmitTree(cx, bce, pn->pn_tree);
6122 811155 : bce->roLexdeps.clearMap();
6123 811155 : pn->pn_names.releaseMap(cx);
6124 811155 : break;
6125 :
6126 : case PNK_IF:
6127 929543 : ok = EmitIf(cx, bce, pn);
6128 929543 : break;
6129 :
6130 : case PNK_SWITCH:
6131 17231 : ok = EmitSwitch(cx, bce, pn);
6132 17231 : break;
6133 :
6134 : case PNK_WHILE:
6135 51638 : ok = EmitWhile(cx, bce, pn, top);
6136 51638 : break;
6137 :
6138 : case PNK_DOWHILE:
6139 383 : ok = EmitDo(cx, bce, pn);
6140 383 : break;
6141 :
6142 : case PNK_FOR:
6143 122103 : ok = EmitFor(cx, bce, pn, top);
6144 122103 : break;
6145 :
6146 : case PNK_BREAK:
6147 53010 : ok = EmitBreak(cx, bce, pn->asBreakStatement().label());
6148 53010 : break;
6149 :
6150 : case PNK_CONTINUE:
6151 38621 : ok = EmitContinue(cx, bce, pn->asContinueStatement().label());
6152 38621 : break;
6153 :
6154 : case PNK_WITH:
6155 630 : ok = EmitWith(cx, bce, pn);
6156 630 : break;
6157 :
6158 : case PNK_TRY:
6159 241021 : if (!EmitTry(cx, bce, pn))
6160 0 : return false;
6161 241021 : break;
6162 :
6163 : case PNK_CATCH:
6164 225154 : if (!EmitCatch(cx, bce, pn))
6165 0 : return false;
6166 225154 : break;
6167 :
6168 : case PNK_VAR:
6169 : case PNK_CONST:
6170 1024826 : if (!EmitVariables(cx, bce, pn, InitializeVars))
6171 0 : return JS_FALSE;
6172 1024826 : break;
6173 :
6174 : case PNK_RETURN:
6175 660086 : ok = EmitReturn(cx, bce, pn);
6176 660086 : break;
6177 :
6178 : #if JS_HAS_GENERATORS
6179 : case PNK_YIELD:
6180 4374 : JS_ASSERT(bce->inFunction());
6181 4374 : if (pn->pn_kid) {
6182 3593 : if (!EmitTree(cx, bce, pn->pn_kid))
6183 0 : return JS_FALSE;
6184 : } else {
6185 781 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
6186 0 : return JS_FALSE;
6187 : }
6188 4374 : if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
6189 0 : return JS_FALSE;
6190 4374 : if (Emit1(cx, bce, JSOP_YIELD) < 0)
6191 0 : return JS_FALSE;
6192 4374 : break;
6193 : #endif
6194 :
6195 : #if JS_HAS_XML_SUPPORT
6196 : case PNK_XMLCURLYEXPR:
6197 0 : JS_ASSERT(pn->isArity(PN_UNARY));
6198 0 : if (!EmitTree(cx, bce, pn->pn_kid))
6199 0 : return JS_FALSE;
6200 0 : if (Emit1(cx, bce, pn->getOp()) < 0)
6201 0 : return JS_FALSE;
6202 0 : break;
6203 : #endif
6204 :
6205 : case PNK_STATEMENTLIST:
6206 2400798 : ok = EmitStatementList(cx, bce, pn, top);
6207 2400798 : break;
6208 :
6209 : case PNK_SEQ:
6210 934 : ok = EmitSyntheticStatements(cx, bce, pn, top);
6211 934 : break;
6212 :
6213 : case PNK_SEMI:
6214 6474702 : ok = EmitStatement(cx, bce, pn);
6215 6474702 : break;
6216 :
6217 : case PNK_COLON:
6218 193 : ok = EmitLabel(cx, bce, pn);
6219 193 : break;
6220 :
6221 : case PNK_COMMA:
6222 : {
6223 : /*
6224 : * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
6225 : * These notes help the decompiler bracket the bytecodes generated
6226 : * from each sub-expression that follows a comma.
6227 : */
6228 2098 : ptrdiff_t off = -1, noteIndex = -1;
6229 4707 : for (ParseNode *pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
6230 4707 : if (!EmitTree(cx, bce, pn2))
6231 0 : return JS_FALSE;
6232 4707 : ptrdiff_t tmp = bce->offset();
6233 4707 : if (noteIndex >= 0) {
6234 2609 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp-off))
6235 0 : return JS_FALSE;
6236 : }
6237 4707 : if (!pn2->pn_next)
6238 : break;
6239 2609 : off = tmp;
6240 2609 : noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
6241 5218 : if (noteIndex < 0 ||
6242 2609 : Emit1(cx, bce, JSOP_POP) < 0) {
6243 0 : return JS_FALSE;
6244 : }
6245 : }
6246 2098 : break;
6247 : }
6248 :
6249 : case PNK_ASSIGN:
6250 : case PNK_ADDASSIGN:
6251 : case PNK_SUBASSIGN:
6252 : case PNK_BITORASSIGN:
6253 : case PNK_BITXORASSIGN:
6254 : case PNK_BITANDASSIGN:
6255 : case PNK_LSHASSIGN:
6256 : case PNK_RSHASSIGN:
6257 : case PNK_URSHASSIGN:
6258 : case PNK_MULASSIGN:
6259 : case PNK_DIVASSIGN:
6260 : case PNK_MODASSIGN:
6261 3018658 : if (!EmitAssignment(cx, bce, pn->pn_left, pn->getOp(), pn->pn_right))
6262 0 : return false;
6263 3018658 : break;
6264 :
6265 : case PNK_CONDITIONAL:
6266 68371 : ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression());
6267 68371 : break;
6268 :
6269 : case PNK_OR:
6270 : case PNK_AND:
6271 229653 : ok = EmitLogical(cx, bce, pn);
6272 229653 : break;
6273 :
6274 : case PNK_ADD:
6275 : case PNK_SUB:
6276 : case PNK_BITOR:
6277 : case PNK_BITXOR:
6278 : case PNK_BITAND:
6279 : case PNK_STRICTEQ:
6280 : case PNK_EQ:
6281 : case PNK_STRICTNE:
6282 : case PNK_NE:
6283 : case PNK_LT:
6284 : case PNK_LE:
6285 : case PNK_GT:
6286 : case PNK_GE:
6287 : case PNK_IN:
6288 : case PNK_INSTANCEOF:
6289 : case PNK_LSH:
6290 : case PNK_RSH:
6291 : case PNK_URSH:
6292 : case PNK_STAR:
6293 : case PNK_DIV:
6294 : case PNK_MOD:
6295 1123155 : if (pn->isArity(PN_LIST)) {
6296 : /* Left-associative operator chain: avoid too much recursion. */
6297 166353 : ParseNode *pn2 = pn->pn_head;
6298 166353 : if (!EmitTree(cx, bce, pn2))
6299 0 : return JS_FALSE;
6300 166353 : JSOp op = pn->getOp();
6301 166353 : while ((pn2 = pn2->pn_next) != NULL) {
6302 2046292 : if (!EmitTree(cx, bce, pn2))
6303 0 : return JS_FALSE;
6304 2046292 : if (Emit1(cx, bce, op) < 0)
6305 0 : return JS_FALSE;
6306 : }
6307 : } else {
6308 : #if JS_HAS_XML_SUPPORT
6309 : unsigned oldflags;
6310 :
6311 : case PNK_DBLCOLON:
6312 956874 : JS_ASSERT(pn->getOp() != JSOP_XMLNAME);
6313 956874 : if (pn->isArity(PN_NAME)) {
6314 72 : if (!EmitTree(cx, bce, pn->expr()))
6315 0 : return JS_FALSE;
6316 72 : if (!EmitAtomOp(cx, pn, pn->getOp(), bce))
6317 0 : return JS_FALSE;
6318 72 : break;
6319 : }
6320 :
6321 : /*
6322 : * Binary :: has a right operand that brackets arbitrary code,
6323 : * possibly including a let (a = b) ... expression. We must clear
6324 : * TCF_IN_FOR_INIT to avoid mis-compiling such beasts.
6325 : */
6326 956802 : oldflags = bce->flags;
6327 956802 : bce->flags &= ~TCF_IN_FOR_INIT;
6328 : #endif
6329 :
6330 : /* Binary operators that evaluate both operands unconditionally. */
6331 956802 : if (!EmitTree(cx, bce, pn->pn_left))
6332 0 : return JS_FALSE;
6333 956802 : if (!EmitTree(cx, bce, pn->pn_right))
6334 0 : return JS_FALSE;
6335 : #if JS_HAS_XML_SUPPORT
6336 956802 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
6337 : #endif
6338 956802 : if (Emit1(cx, bce, pn->getOp()) < 0)
6339 0 : return JS_FALSE;
6340 : }
6341 1123155 : break;
6342 :
6343 : #if JS_HAS_XML_SUPPORT
6344 : case PNK_XMLUNARY:
6345 54 : if (pn->getOp() == JSOP_XMLNAME) {
6346 54 : if (!EmitXMLName(cx, pn, JSOP_XMLNAME, bce))
6347 0 : return false;
6348 : } else {
6349 0 : JSOp op = pn->getOp();
6350 0 : JS_ASSERT(op == JSOP_BINDXMLNAME || op == JSOP_SETXMLNAME);
6351 0 : unsigned oldflags = bce->flags;
6352 0 : bce->flags &= ~TCF_IN_FOR_INIT;
6353 0 : if (!EmitTree(cx, bce, pn->pn_kid))
6354 0 : return false;
6355 0 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
6356 0 : if (Emit1(cx, bce, op) < 0)
6357 0 : return false;
6358 : }
6359 54 : break;
6360 : #endif
6361 :
6362 : case PNK_THROW:
6363 : #if JS_HAS_XML_SUPPORT
6364 : case PNK_AT:
6365 : case PNK_DEFXMLNS:
6366 123773 : JS_ASSERT(pn->isArity(PN_UNARY));
6367 : /* FALL THROUGH */
6368 : #endif
6369 : case PNK_TYPEOF:
6370 : case PNK_VOID:
6371 : case PNK_NOT:
6372 : case PNK_BITNOT:
6373 : case PNK_POS:
6374 : case PNK_NEG:
6375 531783 : ok = EmitUnary(cx, bce, pn);
6376 531783 : break;
6377 :
6378 : case PNK_PREINCREMENT:
6379 : case PNK_PREDECREMENT:
6380 : case PNK_POSTINCREMENT:
6381 : case PNK_POSTDECREMENT:
6382 1737187 : ok = EmitIncOrDec(cx, bce, pn);
6383 1737187 : break;
6384 :
6385 : case PNK_DELETE:
6386 37494 : ok = EmitDelete(cx, bce, pn);
6387 37494 : break;
6388 :
6389 : #if JS_HAS_XML_SUPPORT
6390 : case PNK_FILTER:
6391 : {
6392 63 : JS_ASSERT(!bce->inStrictMode());
6393 :
6394 63 : if (!EmitTree(cx, bce, pn->pn_left))
6395 0 : return JS_FALSE;
6396 63 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0);
6397 63 : if (jmp < 0)
6398 0 : return JS_FALSE;
6399 63 : top = EmitLoopHead(cx, bce, pn->pn_right);
6400 63 : if (top < 0)
6401 0 : return JS_FALSE;
6402 63 : if (!EmitTree(cx, bce, pn->pn_right))
6403 0 : return JS_FALSE;
6404 63 : SetJumpOffsetAt(bce, jmp);
6405 63 : if (!EmitLoopEntry(cx, bce, NULL))
6406 0 : return false;
6407 63 : if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0)
6408 0 : return JS_FALSE;
6409 63 : break;
6410 : }
6411 : #endif
6412 :
6413 : case PNK_DOT:
6414 : /*
6415 : * Pop a stack operand, convert it to object, get a property named by
6416 : * this bytecode's immediate-indexed atom operand, and push its value
6417 : * (not a reference to it).
6418 : */
6419 5841557 : ok = EmitPropOp(cx, pn, pn->getOp(), bce, JS_FALSE);
6420 5841557 : break;
6421 :
6422 : #if JS_HAS_XML_SUPPORT
6423 : case PNK_DBLDOT:
6424 9 : JS_ASSERT(!bce->inStrictMode());
6425 : /* FALL THROUGH */
6426 : #endif
6427 : case PNK_LB:
6428 : /*
6429 : * Pop two operands, convert the left one to object and the right one
6430 : * to property name (atom or tagged int), get the named property, and
6431 : * push its value. Set the "obj" register to the result of ToObject
6432 : * on the left operand.
6433 : */
6434 272637 : ok = EmitElemOp(cx, pn, pn->getOp(), bce);
6435 272637 : break;
6436 :
6437 : case PNK_NEW:
6438 : case PNK_LP:
6439 3331826 : ok = EmitCallOrNew(cx, bce, pn, top);
6440 3331826 : break;
6441 :
6442 : case PNK_LEXICALSCOPE:
6443 366540 : ok = EmitLexicalScope(cx, bce, pn);
6444 366540 : break;
6445 :
6446 : #if JS_HAS_BLOCK_SCOPE
6447 : case PNK_LET:
6448 250177 : ok = pn->isArity(PN_BINARY)
6449 45212 : ? EmitLet(cx, bce, pn)
6450 295389 : : EmitVariables(cx, bce, pn, InitializeVars);
6451 250177 : break;
6452 : #endif /* JS_HAS_BLOCK_SCOPE */
6453 : #if JS_HAS_GENERATORS
6454 : case PNK_ARRAYPUSH: {
6455 : int slot;
6456 :
6457 : /*
6458 : * The array object's stack index is in bce->arrayCompDepth. See below
6459 : * under the array initialiser code generator for array comprehension
6460 : * special casing.
6461 : */
6462 12678 : if (!EmitTree(cx, bce, pn->pn_kid))
6463 0 : return JS_FALSE;
6464 12678 : slot = AdjustBlockSlot(cx, bce, bce->arrayCompDepth);
6465 12678 : if (slot < 0)
6466 0 : return JS_FALSE;
6467 12678 : EMIT_UINT16_IMM_OP(pn->getOp(), slot);
6468 12678 : break;
6469 : }
6470 : #endif
6471 :
6472 : case PNK_RB:
6473 : #if JS_HAS_GENERATORS
6474 : case PNK_ARRAYCOMP:
6475 : #endif
6476 224358 : ok = EmitArray(cx, bce, pn);
6477 224358 : break;
6478 :
6479 : case PNK_RC:
6480 216320 : ok = EmitObject(cx, bce, pn);
6481 216320 : break;
6482 :
6483 : case PNK_NAME:
6484 : /*
6485 : * Cope with a left-over function definition that was replaced by a use
6486 : * of a later function definition of the same name. See FunctionDef and
6487 : * MakeDefIntoUse in Parser.cpp.
6488 : */
6489 12079812 : if (pn->isOp(JSOP_NOP))
6490 0 : break;
6491 12079812 : if (!EmitNameOp(cx, bce, pn, JS_FALSE))
6492 0 : return JS_FALSE;
6493 12079812 : break;
6494 :
6495 : #if JS_HAS_XML_SUPPORT
6496 : case PNK_XMLATTR:
6497 : case PNK_XMLSPACE:
6498 : case PNK_XMLTEXT:
6499 : case PNK_XMLCDATA:
6500 : case PNK_XMLCOMMENT:
6501 335 : JS_ASSERT(!bce->inStrictMode());
6502 : /* FALL THROUGH */
6503 : #endif
6504 : case PNK_STRING:
6505 6904205 : ok = EmitAtomOp(cx, pn, pn->getOp(), bce);
6506 6904205 : break;
6507 :
6508 : case PNK_NUMBER:
6509 5885932 : ok = EmitNumberOp(cx, pn->pn_dval, bce);
6510 5885932 : break;
6511 :
6512 : case PNK_REGEXP:
6513 34050 : JS_ASSERT(pn->isOp(JSOP_REGEXP));
6514 34050 : ok = EmitRegExp(cx, bce->regexpList.index(pn->pn_objbox), bce);
6515 34050 : break;
6516 :
6517 : #if JS_HAS_XML_SUPPORT
6518 : case PNK_ANYNAME:
6519 : #endif
6520 : case PNK_TRUE:
6521 : case PNK_FALSE:
6522 : case PNK_THIS:
6523 : case PNK_NULL:
6524 2387048 : if (Emit1(cx, bce, pn->getOp()) < 0)
6525 0 : return JS_FALSE;
6526 2387048 : break;
6527 :
6528 : case PNK_DEBUGGER:
6529 4397 : if (Emit1(cx, bce, JSOP_DEBUGGER) < 0)
6530 0 : return JS_FALSE;
6531 4397 : break;
6532 :
6533 : #if JS_HAS_XML_SUPPORT
6534 : case PNK_XMLELEM:
6535 : case PNK_XMLLIST:
6536 308 : JS_ASSERT(!bce->inStrictMode());
6537 308 : JS_ASSERT(pn->isKind(PNK_XMLLIST) || pn->pn_count != 0);
6538 :
6539 308 : switch (pn->pn_head ? pn->pn_head->getKind() : PNK_XMLLIST) {
6540 : case PNK_XMLETAGO:
6541 0 : JS_ASSERT(0);
6542 : /* FALL THROUGH */
6543 : case PNK_XMLPTAGC:
6544 : case PNK_XMLSTAGO:
6545 9 : break;
6546 : default:
6547 299 : if (Emit1(cx, bce, JSOP_STARTXML) < 0)
6548 0 : return JS_FALSE;
6549 : }
6550 :
6551 634 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6552 326 : if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0)
6553 0 : return JS_FALSE;
6554 326 : if (!EmitTree(cx, bce, pn2))
6555 0 : return JS_FALSE;
6556 326 : if (pn2 != pn->pn_head && Emit1(cx, bce, JSOP_ADD) < 0)
6557 0 : return JS_FALSE;
6558 : }
6559 :
6560 308 : if (pn->pn_xflags & PNX_XMLROOT) {
6561 308 : if (pn->pn_count == 0) {
6562 0 : JS_ASSERT(pn->isKind(PNK_XMLLIST));
6563 0 : JSAtom *atom = cx->runtime->atomState.emptyAtom;
6564 : jsatomid index;
6565 0 : if (!bce->makeAtomIndex(atom, &index))
6566 0 : return JS_FALSE;
6567 0 : if (!EmitIndex32(cx, JSOP_STRING, index, bce))
6568 0 : return false;
6569 : }
6570 308 : if (Emit1(cx, bce, pn->getOp()) < 0)
6571 0 : return JS_FALSE;
6572 : }
6573 : #ifdef DEBUG
6574 : else
6575 0 : JS_ASSERT(pn->pn_count != 0);
6576 : #endif
6577 308 : break;
6578 :
6579 : case PNK_XMLPTAGC:
6580 : case PNK_XMLSTAGO:
6581 : case PNK_XMLETAGO:
6582 36 : if (!EmitXMLTag(cx, bce, pn))
6583 0 : return false;
6584 36 : break;
6585 :
6586 : case PNK_XMLNAME:
6587 36 : JS_ASSERT(!bce->inStrictMode());
6588 :
6589 36 : if (pn->isArity(PN_LIST)) {
6590 0 : JS_ASSERT(pn->pn_count != 0);
6591 0 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6592 0 : if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0)
6593 0 : return JS_FALSE;
6594 0 : if (!EmitTree(cx, bce, pn2))
6595 0 : return JS_FALSE;
6596 0 : if (pn2 != pn->pn_head && Emit1(cx, bce, JSOP_ADD) < 0)
6597 0 : return JS_FALSE;
6598 : }
6599 : } else {
6600 36 : JS_ASSERT(pn->isArity(PN_NULLARY));
6601 36 : ok = pn->isOp(JSOP_OBJECT)
6602 0 : ? EmitObjectOp(cx, pn->pn_objbox, pn->getOp(), bce)
6603 36 : : EmitAtomOp(cx, pn, pn->getOp(), bce);
6604 : }
6605 36 : break;
6606 :
6607 : case PNK_XMLPI:
6608 0 : if (!EmitXMLProcessingInstruction(cx, bce, pn->asXMLProcessingInstruction()))
6609 0 : return false;
6610 0 : break;
6611 : #endif /* JS_HAS_XML_SUPPORT */
6612 :
6613 : default:
6614 0 : JS_ASSERT(0);
6615 : }
6616 :
6617 : /* bce->emitLevel == 1 means we're last on the stack, so finish up. */
6618 59282303 : if (ok && bce->emitLevel == 1) {
6619 1879816 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.end.lineno))
6620 0 : return JS_FALSE;
6621 : }
6622 :
6623 59282303 : return ok;
6624 : }
6625 :
6626 : static int
6627 69519530 : AllocSrcNote(JSContext *cx, BytecodeEmitter *bce)
6628 : {
6629 69519530 : jssrcnote *notes = bce->notes();
6630 : jssrcnote *newnotes;
6631 69519530 : unsigned index = bce->noteCount();
6632 69519530 : unsigned max = bce->noteLimit();
6633 :
6634 69519530 : if (index == max) {
6635 : size_t newlength;
6636 1046757 : if (!notes) {
6637 1036649 : JS_ASSERT(!index && !max);
6638 1036649 : newlength = SRCNOTE_CHUNK_LENGTH;
6639 1036649 : newnotes = (jssrcnote *) cx->malloc_(SRCNOTE_SIZE(newlength));
6640 : } else {
6641 10108 : JS_ASSERT(index <= max);
6642 10108 : newlength = max * 2;
6643 10108 : newnotes = (jssrcnote *) cx->realloc_(notes, SRCNOTE_SIZE(newlength));
6644 : }
6645 1046757 : if (!newnotes) {
6646 0 : js_ReportOutOfMemory(cx);
6647 0 : return -1;
6648 : }
6649 1046757 : bce->current->notes = newnotes;
6650 1046757 : bce->current->noteLimit = newlength;
6651 : }
6652 :
6653 69519530 : bce->current->noteCount = index + 1;
6654 69519530 : return (int)index;
6655 : }
6656 :
6657 : int
6658 58132804 : frontend::NewSrcNote(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type)
6659 : {
6660 : int index, n;
6661 : jssrcnote *sn;
6662 : ptrdiff_t offset, delta, xdelta;
6663 :
6664 : /*
6665 : * Claim a note slot in bce->notes() by growing it if necessary and then
6666 : * incrementing bce->noteCount().
6667 : */
6668 58132804 : index = AllocSrcNote(cx, bce);
6669 58132804 : if (index < 0)
6670 0 : return -1;
6671 58132804 : sn = &bce->notes()[index];
6672 :
6673 : /*
6674 : * Compute delta from the last annotated bytecode's offset. If it's too
6675 : * big to fit in sn, allocate one or more xdelta notes and reset sn.
6676 : */
6677 58132804 : offset = bce->offset();
6678 58132804 : delta = offset - bce->lastNoteOffset();
6679 58132804 : bce->current->lastNoteOffset = offset;
6680 58132804 : if (delta >= SN_DELTA_LIMIT) {
6681 11386726 : do {
6682 11386726 : xdelta = JS_MIN(delta, SN_XDELTA_MASK);
6683 11386726 : SN_MAKE_XDELTA(sn, xdelta);
6684 11386726 : delta -= xdelta;
6685 11386726 : index = AllocSrcNote(cx, bce);
6686 11386726 : if (index < 0)
6687 0 : return -1;
6688 11386726 : sn = &bce->notes()[index];
6689 : } while (delta >= SN_DELTA_LIMIT);
6690 : }
6691 :
6692 : /*
6693 : * Initialize type and delta, then allocate the minimum number of notes
6694 : * needed for type's arity. Usually, we won't need more, but if an offset
6695 : * does take two bytes, SetSrcNoteOffset will grow bce->notes().
6696 : */
6697 58132804 : SN_MAKE_NOTE(sn, type, delta);
6698 80147428 : for (n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
6699 22014624 : if (NewSrcNote(cx, bce, SRC_NULL) < 0)
6700 0 : return -1;
6701 : }
6702 58132804 : return index;
6703 : }
6704 :
6705 : int
6706 21096554 : frontend::NewSrcNote2(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset)
6707 : {
6708 : int index;
6709 :
6710 21096554 : index = NewSrcNote(cx, bce, type);
6711 21096554 : if (index >= 0) {
6712 21096554 : if (!SetSrcNoteOffset(cx, bce, index, 0, offset))
6713 0 : return -1;
6714 : }
6715 21096554 : return index;
6716 : }
6717 :
6718 : int
6719 17231 : frontend::NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
6720 : ptrdiff_t offset2)
6721 : {
6722 : int index;
6723 :
6724 17231 : index = NewSrcNote(cx, bce, type);
6725 17231 : if (index >= 0) {
6726 17231 : if (!SetSrcNoteOffset(cx, bce, index, 0, offset1))
6727 0 : return -1;
6728 17231 : if (!SetSrcNoteOffset(cx, bce, index, 1, offset2))
6729 0 : return -1;
6730 : }
6731 17231 : return index;
6732 : }
6733 :
6734 : static JSBool
6735 35 : GrowSrcNotes(JSContext *cx, BytecodeEmitter *bce)
6736 : {
6737 35 : size_t newlength = bce->noteLimit() * 2;
6738 35 : jssrcnote *newnotes = (jssrcnote *) cx->realloc_(bce->notes(), newlength);
6739 35 : if (!newnotes) {
6740 0 : js_ReportOutOfMemory(cx);
6741 0 : return JS_FALSE;
6742 : }
6743 35 : bce->current->notes = newnotes;
6744 35 : bce->current->noteLimit = newlength;
6745 35 : return JS_TRUE;
6746 : }
6747 :
6748 : jssrcnote *
6749 61243 : frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
6750 : {
6751 : ptrdiff_t base, limit, newdelta, diff;
6752 : int index;
6753 :
6754 : /*
6755 : * Called only from OptimizeSpanDeps and FinishTakingSrcNotes to add to
6756 : * main script note deltas, and only by a small positive amount.
6757 : */
6758 61243 : JS_ASSERT(bce->current == &bce->main);
6759 61243 : JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
6760 :
6761 61243 : base = SN_DELTA(sn);
6762 61243 : limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
6763 61243 : newdelta = base + delta;
6764 61243 : if (newdelta < limit) {
6765 34086 : SN_SET_DELTA(sn, newdelta);
6766 : } else {
6767 27157 : index = sn - bce->main.notes;
6768 27157 : if (bce->main.noteCount == bce->main.noteLimit) {
6769 0 : if (!GrowSrcNotes(cx, bce))
6770 0 : return NULL;
6771 0 : sn = bce->main.notes + index;
6772 : }
6773 27157 : diff = bce->main.noteCount - index;
6774 27157 : bce->main.noteCount++;
6775 27157 : memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
6776 27157 : SN_MAKE_XDELTA(sn, delta);
6777 27157 : sn++;
6778 : }
6779 61243 : return sn;
6780 : }
6781 :
6782 : static JSBool
6783 22046442 : SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset)
6784 : {
6785 : jssrcnote *sn;
6786 : ptrdiff_t diff;
6787 :
6788 22046442 : if (size_t(offset) > SN_MAX_OFFSET) {
6789 0 : ReportStatementTooLarge(cx, bce);
6790 0 : return JS_FALSE;
6791 : }
6792 :
6793 : /* Find the offset numbered which (i.e., skip exactly which offsets). */
6794 22046442 : sn = &bce->notes()[index];
6795 22046442 : JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
6796 22046442 : JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
6797 22347540 : for (sn++; which; sn++, which--) {
6798 301098 : if (*sn & SN_3BYTE_OFFSET_FLAG)
6799 51713 : sn += 2;
6800 : }
6801 :
6802 : /*
6803 : * See if the new offset requires three bytes either by being too big or if
6804 : * the offset has already been inflated (in which case, we need to stay big
6805 : * to not break the srcnote encoding if this isn't the last srcnote).
6806 : */
6807 22046442 : if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK || (*sn & SN_3BYTE_OFFSET_FLAG)) {
6808 : /* Maybe this offset was already set to a three-byte value. */
6809 1664556 : if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
6810 : /* Losing, need to insert another two bytes for this offset. */
6811 1660398 : index = sn - bce->notes();
6812 :
6813 : /*
6814 : * Test to see if the source note array must grow to accommodate
6815 : * either the first or second byte of additional storage required
6816 : * by this 3-byte offset.
6817 : */
6818 1660398 : if (bce->noteCount() + 1 >= bce->noteLimit()) {
6819 35 : if (!GrowSrcNotes(cx, bce))
6820 0 : return JS_FALSE;
6821 35 : sn = bce->notes() + index;
6822 : }
6823 1660398 : bce->current->noteCount += 2;
6824 :
6825 1660398 : diff = bce->noteCount() - (index + 3);
6826 1660398 : JS_ASSERT(diff >= 0);
6827 1660398 : if (diff > 0)
6828 190470 : memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
6829 : }
6830 1664556 : *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
6831 1664556 : *sn++ = (jssrcnote)(offset >> 8);
6832 : }
6833 22046442 : *sn = (jssrcnote)offset;
6834 22046442 : return JS_TRUE;
6835 : }
6836 :
6837 : #ifdef DEBUG_notme
6838 : #define DEBUG_srcnotesize
6839 : #endif
6840 :
6841 : #ifdef DEBUG_srcnotesize
6842 : #define NBINS 10
6843 : static uint32_t hist[NBINS];
6844 :
6845 : static void
6846 : DumpSrcNoteSizeHist()
6847 : {
6848 : static FILE *fp;
6849 : int i, n;
6850 :
6851 : if (!fp) {
6852 : fp = fopen("/tmp/srcnotes.hist", "w");
6853 : if (!fp)
6854 : return;
6855 : setvbuf(fp, NULL, _IONBF, 0);
6856 : }
6857 : fprintf(fp, "SrcNote size histogram:\n");
6858 : for (i = 0; i < NBINS; i++) {
6859 : fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]);
6860 : for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n)
6861 : fputc('*', fp);
6862 : fputc('\n', fp);
6863 : }
6864 : fputc('\n', fp);
6865 : }
6866 : #endif
6867 :
6868 : /*
6869 : * Fill in the storage at notes with prolog and main srcnotes; the space at
6870 : * notes was allocated using the BytecodeEmitter::countFinalSourceNotes()
6871 : * method from BytecodeEmitter.h. SO DON'T CHANGE THIS FUNCTION WITHOUT AT
6872 : * LEAST CHECKING WHETHER BytecodeEmitter::countFinalSourceNotes() NEEDS
6873 : * CORRESPONDING CHANGES!
6874 : */
6875 : JSBool
6876 1109121 : frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes)
6877 : {
6878 : unsigned prologCount, mainCount, totalCount;
6879 : ptrdiff_t offset, delta;
6880 : jssrcnote *sn;
6881 :
6882 1109121 : JS_ASSERT(bce->current == &bce->main);
6883 :
6884 1109121 : prologCount = bce->prolog.noteCount;
6885 1109121 : if (prologCount && bce->prolog.currentLine != bce->firstLine) {
6886 17407 : bce->switchToProlog();
6887 17407 : if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0)
6888 0 : return false;
6889 17407 : prologCount = bce->prolog.noteCount;
6890 17407 : bce->switchToMain();
6891 : } else {
6892 : /*
6893 : * Either no prolog srcnotes, or no line number change over prolog.
6894 : * We don't need a SRC_SETLINE, but we may need to adjust the offset
6895 : * of the first main note, by adding to its delta and possibly even
6896 : * prepending SRC_XDELTA notes to it to account for prolog bytecodes
6897 : * that came at and after the last annotated bytecode.
6898 : */
6899 1091714 : offset = bce->prologOffset() - bce->prolog.lastNoteOffset;
6900 1091714 : JS_ASSERT(offset >= 0);
6901 1091714 : if (offset > 0 && bce->main.noteCount != 0) {
6902 : /* NB: Use as much of the first main note's delta as we can. */
6903 34086 : sn = bce->main.notes;
6904 : delta = SN_IS_XDELTA(sn)
6905 : ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
6906 34086 : : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
6907 34086 : if (offset < delta)
6908 6911 : delta = offset;
6909 27157 : for (;;) {
6910 61243 : if (!AddToSrcNoteDelta(cx, bce, sn, delta))
6911 0 : return false;
6912 61243 : offset -= delta;
6913 61243 : if (offset == 0)
6914 : break;
6915 27157 : delta = JS_MIN(offset, SN_XDELTA_MASK);
6916 27157 : sn = bce->main.notes;
6917 : }
6918 : }
6919 : }
6920 :
6921 1109121 : mainCount = bce->main.noteCount;
6922 1109121 : totalCount = prologCount + mainCount;
6923 1109121 : if (prologCount)
6924 17407 : PodCopy(notes, bce->prolog.notes, prologCount);
6925 1109121 : PodCopy(notes + prologCount, bce->main.notes, mainCount);
6926 1109121 : SN_MAKE_TERMINATOR(¬es[totalCount]);
6927 :
6928 1109121 : return true;
6929 : }
6930 :
6931 : static JSBool
6932 301592 : NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, unsigned stackDepth, size_t start,
6933 : size_t end)
6934 : {
6935 301592 : JS_ASSERT((unsigned)(uint16_t)stackDepth == stackDepth);
6936 301592 : JS_ASSERT(start <= end);
6937 : JS_ASSERT((size_t)(uint32_t)start == start);
6938 : JS_ASSERT((size_t)(uint32_t)end == end);
6939 :
6940 301592 : TryNode *tryNode = cx->tempLifoAlloc().new_<TryNode>();
6941 301592 : if (!tryNode) {
6942 0 : js_ReportOutOfMemory(cx);
6943 0 : return JS_FALSE;
6944 : }
6945 :
6946 301592 : tryNode->note.kind = kind;
6947 301592 : tryNode->note.stackDepth = (uint16_t)stackDepth;
6948 301592 : tryNode->note.start = (uint32_t)start;
6949 301592 : tryNode->note.length = (uint32_t)(end - start);
6950 301592 : tryNode->prev = bce->lastTryNode;
6951 301592 : bce->lastTryNode = tryNode;
6952 301592 : bce->ntrynotes++;
6953 301592 : return JS_TRUE;
6954 : }
6955 :
6956 : void
6957 141944 : frontend::FinishTakingTryNotes(BytecodeEmitter *bce, JSTryNoteArray *array)
6958 : {
6959 : TryNode *tryNode;
6960 : JSTryNote *tn;
6961 :
6962 141944 : JS_ASSERT(array->length > 0 && array->length == bce->ntrynotes);
6963 141944 : tn = array->vector + array->length;
6964 141944 : tryNode = bce->lastTryNode;
6965 301583 : do {
6966 301583 : *--tn = tryNode->note;
6967 : } while ((tryNode = tryNode->prev) != NULL);
6968 141944 : JS_ASSERT(tn == array->vector);
6969 141944 : }
6970 :
6971 : /*
6972 : * Find the index of the given object for code generator.
6973 : *
6974 : * Since the emitter refers to each parsed object only once, for the index we
6975 : * use the number of already indexes objects. We also add the object to a list
6976 : * to convert the list to a fixed-size array when we complete code generation,
6977 : * see js::CGObjectList::finish below.
6978 : *
6979 : * Most of the objects go to BytecodeEmitter::objectList but for regexp we use
6980 : * a separated BytecodeEmitter::regexpList. In this way the emitted index can
6981 : * be directly used to store and fetch a reference to a cloned RegExp object
6982 : * that shares the same JSRegExp private data created for the object literal in
6983 : * objbox. We need a cloned object to hold lastIndex and other direct
6984 : * properties that should not be shared among threads sharing a precompiled
6985 : * function or script.
6986 : *
6987 : * If the code being compiled is function code, allocate a reserved slot in
6988 : * the cloned function object that shares its precompiled script with other
6989 : * cloned function objects and with the compiler-created clone-parent. There
6990 : * are nregexps = script->regexps()->length such reserved slots in each
6991 : * function object cloned from fun->object. NB: during compilation, a funobj
6992 : * slots element must never be allocated, because JSObject::allocSlot could
6993 : * hand out one of the slots that should be given to a regexp clone.
6994 : *
6995 : * If the code being compiled is global code, the cloned regexp are stored in
6996 : * fp->vars slot and to protect regexp slots from GC we set fp->nvars to
6997 : * nregexps.
6998 : *
6999 : * The slots initially contain undefined or null. We populate them lazily when
7000 : * JSOP_REGEXP is executed for the first time.
7001 : *
7002 : * Why clone regexp objects? ECMA specifies that when a regular expression
7003 : * literal is scanned, a RegExp object is created. In the spec, compilation
7004 : * and execution happen indivisibly, but in this implementation and many of
7005 : * its embeddings, code is precompiled early and re-executed in multiple
7006 : * threads, or using multiple global objects, or both, for efficiency.
7007 : *
7008 : * In such cases, naively following ECMA leads to wrongful sharing of RegExp
7009 : * objects, which makes for collisions on the lastIndex property (especially
7010 : * for global regexps) and on any ad-hoc properties. Also, __proto__ refers to
7011 : * the pre-compilation prototype, a pigeon-hole problem for instanceof tests.
7012 : */
7013 : unsigned
7014 1475466 : CGObjectList::index(ObjectBox *objbox)
7015 : {
7016 1475466 : JS_ASSERT(!objbox->emitLink);
7017 1475466 : objbox->emitLink = lastbox;
7018 1475466 : lastbox = objbox;
7019 1475466 : return length++;
7020 : }
7021 :
7022 : void
7023 402694 : CGObjectList::finish(JSObjectArray *array)
7024 : {
7025 402694 : JS_ASSERT(length <= INDEX_LIMIT);
7026 402694 : JS_ASSERT(length == array->length);
7027 :
7028 402694 : js::HeapPtrObject *cursor = array->vector + array->length;
7029 402694 : ObjectBox *objbox = lastbox;
7030 1496534 : do {
7031 1496534 : --cursor;
7032 1496534 : JS_ASSERT(!*cursor);
7033 1496534 : *cursor = objbox->object;
7034 : } while ((objbox = objbox->emitLink) != NULL);
7035 402694 : JS_ASSERT(cursor == array->vector);
7036 402694 : }
7037 :
7038 : void
7039 12798 : GCConstList::finish(JSConstArray *array)
7040 : {
7041 12798 : JS_ASSERT(array->length == list.length());
7042 12798 : Value *src = list.begin(), *srcend = list.end();
7043 12798 : HeapValue *dst = array->vector;
7044 68100 : for (; src != srcend; ++src, ++dst)
7045 55302 : *dst = *src;
7046 12798 : }
7047 :
7048 : /*
7049 : * We should try to get rid of offsetBias (always 0 or 1, where 1 is
7050 : * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL.
7051 : */
7052 : JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
7053 : {"null", 0},
7054 : {"if", 0},
7055 : {"if-else", 2},
7056 : {"for", 3},
7057 : {"while", 1},
7058 : {"continue", 0},
7059 : {"decl", 1},
7060 : {"pcdelta", 1},
7061 : {"assignop", 0},
7062 : {"cond", 1},
7063 : {"brace", 1},
7064 : {"hidden", 0},
7065 : {"pcbase", 1},
7066 : {"label", 1},
7067 : {"labelbrace", 1},
7068 : {"endbrace", 0},
7069 : {"break2label", 1},
7070 : {"cont2label", 1},
7071 : {"switch", 2},
7072 : {"funcdef", 1},
7073 : {"catch", 1},
7074 : {"unused", -1},
7075 : {"newline", 0},
7076 : {"setline", 1},
7077 : {"xdelta", 0},
7078 : };
7079 :
7080 : JS_FRIEND_API(unsigned)
7081 138829626 : js_SrcNoteLength(jssrcnote *sn)
7082 : {
7083 : unsigned arity;
7084 : jssrcnote *base;
7085 :
7086 138829626 : arity = (int)js_SrcNoteSpec[SN_TYPE(sn)].arity;
7087 281625785 : for (base = sn++; arity; sn++, arity--) {
7088 142796159 : if (*sn & SN_3BYTE_OFFSET_FLAG)
7089 6319703 : sn += 2;
7090 : }
7091 138829626 : return sn - base;
7092 : }
7093 :
7094 : JS_FRIEND_API(ptrdiff_t)
7095 4777950 : js_GetSrcNoteOffset(jssrcnote *sn, unsigned which)
7096 : {
7097 : /* Find the offset numbered which (i.e., skip exactly which offsets). */
7098 4777950 : JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
7099 4777950 : JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
7100 4782153 : for (sn++; which; sn++, which--) {
7101 4203 : if (*sn & SN_3BYTE_OFFSET_FLAG)
7102 9 : sn += 2;
7103 : }
7104 4777950 : if (*sn & SN_3BYTE_OFFSET_FLAG) {
7105 1757284 : return (ptrdiff_t)(((uint32_t)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
7106 1757284 : | (sn[1] << 8)
7107 3514568 : | sn[2]);
7108 : }
7109 3020666 : return (ptrdiff_t)*sn;
7110 : }
|