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 1107472 : 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 1107472 : typesetCount(0)
121 : {
122 1107472 : flags = TCF_COMPILING;
123 1107472 : memset(&prolog, 0, sizeof prolog);
124 1107472 : memset(&main, 0, sizeof main);
125 1107472 : current = &main;
126 1107472 : firstLine = prolog.currentLine = main.currentLine = lineno;
127 1107472 : }
128 :
129 : bool
130 1107472 : BytecodeEmitter::init(JSContext *cx, TreeContext::InitBehavior ib)
131 : {
132 1107472 : roLexdeps.init();
133 1107472 : return TreeContext::init(cx, ib) && constMap.init() && atomIndices.ensureMap(cx);
134 : }
135 :
136 2214944 : BytecodeEmitter::~BytecodeEmitter()
137 : {
138 1107472 : JSContext *cx = parser->context;
139 :
140 1107472 : cx->free_(prolog.base);
141 1107472 : cx->free_(prolog.notes);
142 1107472 : cx->free_(main.base);
143 1107472 : cx->free_(main.notes);
144 1107472 : }
145 :
146 : static ptrdiff_t
147 107900251 : EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
148 : {
149 107900251 : jsbytecode *base = bce->base();
150 : jsbytecode *newbase;
151 107900251 : jsbytecode *next = bce->next();
152 107900251 : jsbytecode *limit = bce->limit();
153 107900251 : ptrdiff_t offset = next - base;
154 107900251 : size_t minlength = offset + delta;
155 :
156 107900251 : if (next + delta > limit) {
157 : size_t newlength;
158 1212343 : if (!base) {
159 1158312 : JS_ASSERT(!next && !limit);
160 1158312 : newlength = BYTECODE_CHUNK_LENGTH;
161 1158312 : if (newlength < minlength) /* make it bigger if necessary */
162 0 : newlength = RoundUpPow2(minlength);
163 1158312 : newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength));
164 : } else {
165 54031 : JS_ASSERT(base <= next && next <= limit);
166 54031 : newlength = (limit - base) * 2;
167 54031 : if (newlength < minlength) /* make it bigger if necessary */
168 0 : newlength = RoundUpPow2(minlength);
169 54031 : newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength));
170 : }
171 1212343 : if (!newbase) {
172 0 : js_ReportOutOfMemory(cx);
173 0 : return -1;
174 : }
175 1212343 : JS_ASSERT(newlength >= size_t(offset + delta));
176 1212343 : bce->current->base = newbase;
177 1212343 : bce->current->limit = newbase + newlength;
178 1212343 : bce->current->next = newbase + offset;
179 : }
180 107900251 : return offset;
181 : }
182 :
183 : static StaticBlockObject &
184 446099 : CurrentBlock(BytecodeEmitter *bce)
185 : {
186 446099 : JS_ASSERT(bce->topStmt->type == STMT_BLOCK || bce->topStmt->type == STMT_SWITCH);
187 446099 : JS_ASSERT(bce->topStmt->blockObj->isStaticBlock());
188 446099 : return *bce->topStmt->blockObj;
189 : }
190 :
191 : static void
192 107900251 : UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
193 : {
194 107900251 : jsbytecode *pc = bce->code(target);
195 107900251 : JSOp op = (JSOp) *pc;
196 107900251 : const JSCodeSpec *cs = &js_CodeSpec[op];
197 :
198 :
199 107900251 : 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 9023056 : ((cs->format & JOF_TMPSLOT_MASK) >> JOF_TMPSLOT_SHIFT);
206 9023056 : if (depth > bce->maxStackDepth)
207 1161466 : 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 107900251 : if (op == JSOP_ENTERBLOCK) {
217 366155 : nuses = 0;
218 366155 : ndefs = CurrentBlock(bce).slotCount();
219 107534096 : } else if (op == JSOP_ENTERLET0) {
220 45060 : nuses = ndefs = CurrentBlock(bce).slotCount();
221 107489036 : } else if (op == JSOP_ENTERLET1) {
222 34884 : nuses = ndefs = CurrentBlock(bce).slotCount() + 1;
223 : } else {
224 107454152 : nuses = StackUses(NULL, pc);
225 107454152 : ndefs = StackDefs(NULL, pc);
226 : }
227 :
228 107900251 : bce->stackDepth -= nuses;
229 107900251 : JS_ASSERT(bce->stackDepth >= 0);
230 107900251 : bce->stackDepth += ndefs;
231 107900251 : if ((unsigned)bce->stackDepth > bce->maxStackDepth)
232 3138368 : bce->maxStackDepth = bce->stackDepth;
233 107900251 : }
234 :
235 : static inline void
236 1661587 : UpdateDecomposeLength(BytecodeEmitter *bce, unsigned start)
237 : {
238 1661587 : unsigned end = bce->offset();
239 1661587 : JS_ASSERT(unsigned(end - start) < 256);
240 1661587 : bce->code(start)[-1] = end - start;
241 1661587 : }
242 :
243 : ptrdiff_t
244 51307698 : frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op)
245 : {
246 51307698 : JS_ASSERT_IF(op == JSOP_ARGUMENTS, !bce->mayOverwriteArguments());
247 :
248 51307698 : ptrdiff_t offset = EmitCheck(cx, bce, 1);
249 :
250 51307698 : if (offset >= 0) {
251 51307698 : *bce->current->next++ = (jsbytecode)op;
252 51307698 : UpdateDepth(cx, bce, offset);
253 : }
254 51307698 : return offset;
255 : }
256 :
257 : ptrdiff_t
258 6494030 : frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
259 : {
260 6494030 : ptrdiff_t offset = EmitCheck(cx, bce, 2);
261 :
262 6494030 : if (offset >= 0) {
263 6494030 : jsbytecode *next = bce->next();
264 6494030 : next[0] = (jsbytecode)op;
265 6494030 : next[1] = op1;
266 6494030 : bce->current->next = next + 2;
267 6494030 : UpdateDepth(cx, bce, offset);
268 : }
269 6494030 : return offset;
270 : }
271 :
272 : ptrdiff_t
273 18177279 : frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
274 : jsbytecode op2)
275 : {
276 18177279 : ptrdiff_t offset = EmitCheck(cx, bce, 3);
277 :
278 18177279 : if (offset >= 0) {
279 18177279 : jsbytecode *next = bce->next();
280 18177279 : next[0] = (jsbytecode)op;
281 18177279 : next[1] = op1;
282 18177279 : next[2] = op2;
283 18177279 : bce->current->next = next + 3;
284 18177279 : UpdateDepth(cx, bce, offset);
285 : }
286 18177279 : return offset;
287 : }
288 :
289 : ptrdiff_t
290 273482 : frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
291 : {
292 273482 : ptrdiff_t length = 1 + (ptrdiff_t)extra;
293 273482 : ptrdiff_t offset = EmitCheck(cx, bce, length);
294 :
295 273482 : if (offset >= 0) {
296 273482 : jsbytecode *next = bce->next();
297 273482 : *next = (jsbytecode)op;
298 273482 : memset(next + 1, 0, BYTECODE_SIZE(extra));
299 273482 : 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 273482 : if (js_CodeSpec[op].nuses >= 0)
306 273482 : UpdateDepth(cx, bce, offset);
307 : }
308 273482 : return offset;
309 : }
310 :
311 : static ptrdiff_t
312 2486200 : EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
313 : {
314 2486200 : ptrdiff_t offset = EmitCheck(cx, bce, 5);
315 :
316 2486200 : if (offset >= 0) {
317 2486200 : jsbytecode *next = bce->next();
318 2486200 : next[0] = (jsbytecode)op;
319 2486200 : SET_JUMP_OFFSET(next, off);
320 2486200 : bce->current->next = next + 5;
321 2486200 : UpdateDepth(cx, bce, offset);
322 : }
323 2486200 : 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 1099598 : 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 1099598 : 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 1099598 : tc->staticLevel = staticLevel;
402 1099598 : return true;
403 : }
404 :
405 : bool
406 2563204 : frontend::GenerateBlockId(TreeContext *tc, uint32_t &blockid)
407 : {
408 2563204 : 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 2563204 : blockid = tc->blockidGen++;
414 2563204 : return true;
415 : }
416 :
417 : void
418 7747150 : frontend::PushStatement(TreeContext *tc, StmtInfo *stmt, StmtType type, ptrdiff_t top)
419 : {
420 7747150 : stmt->type = type;
421 7747150 : stmt->flags = 0;
422 7747150 : stmt->blockid = tc->blockid();
423 7747150 : SET_STATEMENT_TOP(stmt, top);
424 7747150 : stmt->label = NULL;
425 7747150 : JS_ASSERT(!stmt->blockObj);
426 7747150 : stmt->down = tc->topStmt;
427 7747150 : tc->topStmt = stmt;
428 7747150 : if (STMT_LINKS_SCOPE(stmt)) {
429 1323 : stmt->downScope = tc->topScopeStmt;
430 1323 : tc->topScopeStmt = stmt;
431 : } else {
432 7745827 : stmt->downScope = NULL;
433 : }
434 7747150 : }
435 :
436 : void
437 762188 : frontend::PushBlockScope(TreeContext *tc, StmtInfo *stmt, StaticBlockObject &blockObj, ptrdiff_t top)
438 : {
439 762188 : PushStatement(tc, stmt, STMT_BLOCK, top);
440 762188 : stmt->flags |= SIF_SCOPE;
441 762188 : blockObj.setEnclosingBlock(tc->blockChain);
442 762188 : stmt->downScope = tc->topScopeStmt;
443 762188 : tc->topScopeStmt = stmt;
444 762188 : tc->blockChain = &blockObj;
445 762188 : stmt->blockObj = &blockObj;
446 762188 : }
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 748534 : EmitBackPatchOp(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t *lastp)
454 : {
455 : ptrdiff_t offset, delta;
456 :
457 748534 : offset = bce->offset();
458 748534 : delta = offset - *lastp;
459 748534 : *lastp = offset;
460 748534 : JS_ASSERT(delta > 0);
461 748534 : 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 2565495 : UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, unsigned line)
497 : {
498 2565495 : UPDATE_LINE_NUMBER_NOTES(cx, bce, line);
499 2565495 : return JS_TRUE;
500 : }
501 :
502 : static ptrdiff_t
503 173923 : EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
504 : {
505 173923 : 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 119339 : JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
512 119339 : if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
513 64907 : nextpn = nextpn->pn_head;
514 119339 : if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
515 0 : return -1;
516 : }
517 :
518 173923 : return Emit1(cx, bce, JSOP_LOOPHEAD);
519 : }
520 :
521 : static bool
522 173923 : EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
523 : {
524 173923 : if (nextpn) {
525 : /* Update the line number, as for LOOPHEAD. */
526 118894 : JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
527 118894 : if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
528 1090 : nextpn = nextpn->pn_head;
529 118894 : if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
530 0 : return false;
531 : }
532 :
533 173923 : 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 52772454 : CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op)
542 : {
543 52772454 : if (js_CodeSpec[op].format & JOF_TYPESET) {
544 23248434 : if (bce->typesetCount < UINT16_MAX)
545 23248434 : bce->typesetCount++;
546 : }
547 52772454 : }
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 6829 : PopIterator(JSContext *cx, BytecodeEmitter *bce)
602 : {
603 6829 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
604 0 : return false;
605 6829 : if (Emit1(cx, bce, JSOP_ENDITER) < 0)
606 0 : return false;
607 6829 : return true;
608 : }
609 :
610 : /*
611 : * Emit additional bytecode(s) for non-local jumps.
612 : */
613 : static JSBool
614 906802 : 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 906802 : int depth = bce->stackDepth;
623 906802 : int npops = 0;
624 :
625 : #define FLUSH_POPS() if (npops && !FlushPops(cx, bce, &npops)) return JS_FALSE
626 :
627 2493330 : for (StmtInfo *stmt = bce->topStmt; stmt != toStmt; stmt = stmt->down) {
628 1592503 : switch (stmt->type) {
629 : case STMT_FINALLY:
630 7156 : FLUSH_POPS();
631 7156 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
632 0 : return JS_FALSE;
633 7156 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &GOSUBS(*stmt)) < 0)
634 0 : return JS_FALSE;
635 7156 : 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 3270 : FLUSH_POPS();
648 3270 : if (!PopIterator(cx, bce))
649 0 : return JS_FALSE;
650 3270 : 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 1592503 : if (stmt->flags & SIF_SCOPE) {
664 122020 : FLUSH_POPS();
665 122020 : unsigned blockObjCount = stmt->blockObj->slotCount();
666 122020 : 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 9534 : JS_ASSERT(stmt->down->type == STMT_FOR_IN_LOOP);
673 9534 : stmt = stmt->down;
674 9534 : if (stmt == toStmt)
675 5975 : break;
676 3559 : if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
677 0 : return JS_FALSE;
678 3559 : if (!PopIterator(cx, bce))
679 0 : return JS_FALSE;
680 3559 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
681 0 : return JS_FALSE;
682 3559 : EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
683 : } else {
684 : /* There is a Block object with locals on the stack to pop. */
685 112486 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
686 0 : return JS_FALSE;
687 112486 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
688 : }
689 : }
690 : }
691 :
692 906802 : FLUSH_POPS();
693 906802 : bce->stackDepth = depth;
694 906802 : return JS_TRUE;
695 :
696 : #undef FLUSH_POPS
697 : }
698 :
699 : static const jsatomid INVALID_ATOMID = -1;
700 :
701 : static ptrdiff_t
702 247815 : 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 247815 : if (!EmitNonLocalJumpFixup(cx, bce, toStmt))
708 0 : return -1;
709 :
710 247815 : if (labelIndex != INVALID_ATOMID)
711 136 : index = NewSrcNote2(cx, bce, noteType, ptrdiff_t(labelIndex));
712 247679 : else if (noteType != SRC_NULL)
713 91448 : index = NewSrcNote(cx, bce, noteType);
714 : else
715 156231 : index = 0;
716 247815 : if (index < 0)
717 0 : return -1;
718 :
719 247815 : return EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, lastp);
720 : }
721 :
722 : static JSBool
723 8180027 : BackPatch(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t last, jsbytecode *target, jsbytecode op)
724 : {
725 : jsbytecode *pc, *stop;
726 : ptrdiff_t delta, span;
727 :
728 8180027 : pc = bce->code(last);
729 8180027 : stop = bce->code(-1);
730 17108588 : while (pc != stop) {
731 748534 : delta = GET_JUMP_OFFSET(pc);
732 748534 : span = target - pc;
733 748534 : SET_JUMP_OFFSET(pc, span);
734 748534 : *pc = op;
735 748534 : pc -= delta;
736 : }
737 8180027 : return JS_TRUE;
738 : }
739 :
740 : void
741 7746374 : frontend::PopStatementTC(TreeContext *tc)
742 : {
743 7746374 : StmtInfo *stmt = tc->topStmt;
744 7746374 : tc->topStmt = stmt->down;
745 7746374 : if (STMT_LINKS_SCOPE(stmt)) {
746 895791 : tc->topScopeStmt = stmt->downScope;
747 895791 : if (stmt->flags & SIF_SCOPE)
748 894468 : tc->blockChain = stmt->blockObj->enclosingBlock();
749 : }
750 7746374 : }
751 :
752 : JSBool
753 4199387 : frontend::PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
754 : {
755 4199387 : StmtInfo *stmt = bce->topStmt;
756 12116685 : if (!STMT_IS_TRYING(stmt) &&
757 3958649 : (!BackPatch(cx, bce, stmt->breaks, bce->next(), JSOP_GOTO) ||
758 3958649 : !BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
759 : {
760 0 : return JS_FALSE;
761 : }
762 4199387 : PopStatementTC(bce);
763 4199387 : return JS_TRUE;
764 : }
765 :
766 : JSBool
767 168446 : frontend::DefineCompileTimeConstant(JSContext *cx, BytecodeEmitter *bce, JSAtom *atom, ParseNode *pn)
768 : {
769 : /* XXX just do numbers for now */
770 168446 : if (pn->isKind(PNK_NUMBER)) {
771 7952 : if (!bce->constMap.put(atom, NumberValue(pn->pn_dval)))
772 0 : return JS_FALSE;
773 : }
774 168446 : return JS_TRUE;
775 : }
776 :
777 : StmtInfo *
778 14960153 : frontend::LexicalLookup(TreeContext *tc, JSAtom *atom, int *slotp, StmtInfo *stmt)
779 : {
780 14960153 : if (!stmt)
781 14960153 : stmt = tc->topScopeStmt;
782 17185887 : for (; stmt; stmt = stmt->downScope) {
783 3124473 : if (stmt->type == STMT_WITH)
784 783 : break;
785 :
786 : /* Skip "maybe scope" statements that don't contain let bindings. */
787 3123690 : if (!(stmt->flags & SIF_SCOPE))
788 0 : continue;
789 :
790 3123690 : StaticBlockObject &blockObj = *stmt->blockObj;
791 3123690 : const Shape *shape = blockObj.nativeLookup(tc->parser->context, ATOM_TO_JSID(atom));
792 3123690 : if (shape) {
793 897956 : JS_ASSERT(shape->hasShortID());
794 :
795 897956 : if (slotp)
796 0 : *slotp = blockObj.stackDepth() + shape->shortid();
797 897956 : return stmt;
798 : }
799 : }
800 :
801 14062197 : if (slotp)
802 0 : *slotp = -1;
803 14062197 : 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 2356 : 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 2356 : constp->setMagic(JS_NO_CONSTANT);
819 6994 : do {
820 4689 : if (bce->inFunction() || bce->compileAndGo()) {
821 : /* XXX this will need revising if 'const' becomes block-scoped. */
822 2415 : StmtInfo *stmt = LexicalLookup(bce, atom, NULL);
823 2415 : if (stmt)
824 0 : return JS_TRUE;
825 :
826 2415 : 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 2406 : if (bce->inFunction()) {
840 2352 : 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 2333 : } while (bce->parent && (bce = bce->parent->asBytecodeEmitter()));
865 2347 : return JS_TRUE;
866 : }
867 :
868 : static bool
869 2586896 : EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
870 : {
871 2586896 : const size_t len = 1 + UINT32_INDEX_LEN;
872 2586896 : JS_ASSERT(len == size_t(js_CodeSpec[op].length));
873 2586896 : ptrdiff_t offset = EmitCheck(cx, bce, len);
874 2586896 : if (offset < 0)
875 0 : return false;
876 :
877 2586896 : jsbytecode *next = bce->next();
878 2586896 : next[0] = jsbytecode(op);
879 2586896 : SET_UINT32_INDEX(next, index);
880 2586896 : bce->current->next = next + len;
881 2586896 : UpdateDepth(cx, bce, offset);
882 2586896 : CheckTypeSet(cx, bce, op);
883 2586896 : return true;
884 : }
885 :
886 : static bool
887 26325189 : EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
888 : {
889 26325189 : const size_t len = js_CodeSpec[op].length;
890 26325189 : JS_ASSERT(len >= 1 + UINT32_INDEX_LEN);
891 26325189 : ptrdiff_t offset = EmitCheck(cx, bce, len);
892 26325189 : if (offset < 0)
893 0 : return false;
894 :
895 26325189 : jsbytecode *next = bce->next();
896 26325189 : next[0] = jsbytecode(op);
897 26325189 : SET_UINT32_INDEX(next, index);
898 26325189 : bce->current->next = next + len;
899 26325189 : UpdateDepth(cx, bce, offset);
900 26325189 : CheckTypeSet(cx, bce, op);
901 26325189 : return true;
902 : }
903 :
904 : static bool
905 24962343 : EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
906 : {
907 24962343 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
908 :
909 24962343 : if (op == JSOP_GETPROP && atom == cx->runtime->atomState.lengthAtom) {
910 : /* Specialize length accesses for the interpreter. */
911 162943 : op = JSOP_LENGTH;
912 : }
913 :
914 : jsatomid index;
915 24962343 : if (!bce->makeAtomIndex(atom, &index))
916 0 : return false;
917 :
918 24962343 : return EmitIndexOp(cx, op, index, bce);
919 : }
920 :
921 : static bool
922 24962226 : EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
923 : {
924 24962226 : JS_ASSERT(pn->pn_atom != NULL);
925 24962226 : return EmitAtomOp(cx, pn->pn_atom, op, bce);
926 : }
927 :
928 : static bool
929 22603 : EmitAtomIncDec(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
930 : {
931 22603 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
932 22603 : JS_ASSERT(js_CodeSpec[op].format & (JOF_INC | JOF_DEC));
933 :
934 : jsatomid index;
935 22603 : if (!bce->makeAtomIndex(atom, &index))
936 0 : return false;
937 :
938 22603 : const size_t len = 1 + UINT32_INDEX_LEN + 1;
939 22603 : JS_ASSERT(size_t(js_CodeSpec[op].length) == len);
940 22603 : ptrdiff_t offset = EmitCheck(cx, bce, len);
941 22603 : if (offset < 0)
942 0 : return false;
943 :
944 22603 : jsbytecode *next = bce->next();
945 22603 : next[0] = jsbytecode(op);
946 22603 : SET_UINT32_INDEX(next, index);
947 22603 : bce->current->next = next + len;
948 22603 : UpdateDepth(cx, bce, offset);
949 22603 : CheckTypeSet(cx, bce, op);
950 22603 : return true;
951 : }
952 :
953 : static bool
954 913542 : EmitFunctionOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
955 : {
956 913542 : return EmitIndex32(cx, op, index, bce);
957 : }
958 :
959 : static bool
960 453353 : EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
961 : {
962 453353 : JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
963 453353 : return EmitIndex32(cx, op, bce->objectList.index(objbox), bce);
964 : }
965 :
966 : static bool
967 33976 : EmitRegExp(JSContext *cx, uint32_t index, BytecodeEmitter *bce)
968 : {
969 33976 : return EmitIndex32(cx, JSOP_REGEXP, index, bce);
970 : }
971 :
972 : static bool
973 40925 : EmitSlotObjectOp(JSContext *cx, JSOp op, unsigned slot, uint32_t index, BytecodeEmitter *bce)
974 : {
975 40925 : JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTOBJECT);
976 :
977 40925 : ptrdiff_t off = EmitN(cx, bce, op, SLOTNO_LEN + UINT32_INDEX_LEN);
978 40925 : if (off < 0)
979 0 : return false;
980 :
981 40925 : jsbytecode *pc = bce->code(off);
982 40925 : SET_SLOTNO(pc, slot);
983 40925 : pc += SLOTNO_LEN;
984 40925 : SET_UINT32_INDEX(pc, index);
985 40925 : 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 2072768 : BytecodeEmitter::shouldNoteClosedName(ParseNode *pn)
998 : {
999 2072768 : 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 460078 : AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
1012 : {
1013 460078 : JS_ASSERT((unsigned) slot < bce->maxStackDepth);
1014 460078 : if (bce->inFunction()) {
1015 425190 : slot += bce->bindings.countVars();
1016 425190 : 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 460078 : return slot;
1023 : }
1024 :
1025 : static bool
1026 446099 : EmitEnterBlock(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSOp op)
1027 : {
1028 446099 : JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
1029 446099 : if (!EmitObjectOp(cx, pn->pn_objbox, op, bce))
1030 0 : return false;
1031 :
1032 446099 : StaticBlockObject &blockObj = pn->pn_objbox->object->asStaticBlock();
1033 :
1034 : int depth = bce->stackDepth -
1035 446099 : (blockObj.slotCount() + ((op == JSOP_ENTERLET1) ? 1 : 0));
1036 446099 : JS_ASSERT(depth >= 0);
1037 :
1038 446099 : blockObj.setStackDepth(depth);
1039 :
1040 446099 : int depthPlusFixed = AdjustBlockSlot(cx, bce, depth);
1041 446099 : if (depthPlusFixed < 0)
1042 0 : return false;
1043 :
1044 1197750 : for (unsigned i = 0; i < blockObj.slotCount(); i++) {
1045 751651 : Definition *dn = blockObj.maybeDefinitionParseNode(i);
1046 751651 : blockObj.poisonDefinitionParseNode(i);
1047 :
1048 : /* Beware the empty destructuring dummy. */
1049 751651 : if (!dn) {
1050 3942 : JS_ASSERT(i + 1 <= blockObj.slotCount());
1051 3942 : continue;
1052 : }
1053 :
1054 747709 : JS_ASSERT(dn->isDefn());
1055 747709 : JS_ASSERT(unsigned(dn->frameSlot() + depthPlusFixed) < JS_BIT(16));
1056 747709 : dn->pn_cookie.set(dn->pn_cookie.level(), uint16_t(dn->frameSlot() + depthPlusFixed));
1057 : #ifdef DEBUG
1058 1786062 : for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
1059 1038353 : JS_ASSERT(pnu->pn_lexdef == dn);
1060 1038353 : JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
1061 1038353 : 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 886357 : if ((bce->flags & TCF_FUN_EXTENSIBLE_SCOPE) ||
1072 440258 : bce->bindings.extensibleParents()) {
1073 8516 : Shape *newShape = Shape::setExtensibleParents(cx, blockObj.lastProperty());
1074 8516 : if (!newShape)
1075 0 : return false;
1076 8516 : blockObj.setLastPropertyInfallible(newShape);
1077 : }
1078 :
1079 446099 : 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 12020775 : TryConvertToGname(BytecodeEmitter *bce, ParseNode *pn, JSOp *op)
1105 : {
1106 17703267 : if (bce->compileAndGo() &&
1107 : bce->globalScope->globalObj &&
1108 1895310 : !bce->mightAliasLocals() &&
1109 1893591 : !pn->isDeoptimized() &&
1110 1893591 : !(bce->flags & TCF_STRICT_MODE_CODE)) {
1111 1892973 : switch (*op) {
1112 1722633 : case JSOP_NAME: *op = JSOP_GETGNAME; break;
1113 117815 : case JSOP_SETNAME: *op = JSOP_SETGNAME; break;
1114 1552 : case JSOP_INCNAME: *op = JSOP_INCGNAME; break;
1115 4786 : 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 45962 : return false;
1122 0 : default: JS_NOT_REACHED("gname");
1123 : }
1124 1847011 : return true;
1125 : }
1126 10127802 : 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 24159 : 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 24159 : JS_ASSERT(pn->pn_cookie.isFree());
1137 :
1138 24159 : if (bce->mightAliasLocals())
1139 54 : return true;
1140 :
1141 24105 : GlobalScope *globalScope = bce->globalScope;
1142 :
1143 : jsatomid index;
1144 24105 : 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 24105 : AtomIndexPtr p = globalScope->names.lookup(atom);
1148 24105 : JS_ASSERT(!!p);
1149 24105 : 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 24105 : if (!bce->addGlobalUse(atom, index, &pn->pn_cookie))
1167 0 : return false;
1168 :
1169 24105 : if (!pn->pn_cookie.isFree())
1170 24105 : pn->pn_dflags |= PND_BOUND;
1171 :
1172 24105 : return true;
1173 : }
1174 :
1175 : // See BindKnownGlobal()'s comment.
1176 : static bool
1177 206436 : BindGlobal(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSAtom *atom)
1178 : {
1179 206436 : pn->pn_cookie.makeFree();
1180 :
1181 : Definition *dn;
1182 206436 : if (pn->isUsed()) {
1183 21744 : dn = pn->pn_lexdef;
1184 : } else {
1185 184692 : if (!pn->isDefn())
1186 0 : return true;
1187 184692 : dn = (Definition *)pn;
1188 : }
1189 :
1190 : // Only optimize for defined globals.
1191 206436 : if (!dn->isGlobal())
1192 182277 : return true;
1193 :
1194 24159 : 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 22374243 : BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
1216 : {
1217 : Definition *dn;
1218 : JSOp op;
1219 : JSAtom *atom;
1220 : Definition::Kind dn_kind;
1221 :
1222 22374243 : JS_ASSERT(pn->isKind(PNK_NAME));
1223 :
1224 : /* Idempotency tests come first, since we may be called more than once. */
1225 22374243 : if (pn->pn_dflags & PND_BOUND)
1226 4224157 : return JS_TRUE;
1227 :
1228 : /* No cookie initialized for these two, they're pre-bound by definition. */
1229 18150086 : 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 18150086 : if (pn->isUsed()) {
1236 17837724 : JS_ASSERT(pn->pn_cookie.isFree());
1237 17837724 : dn = pn->pn_lexdef;
1238 17837724 : JS_ASSERT(dn->isDefn());
1239 17837724 : if (pn->isDeoptimized())
1240 16601 : return JS_TRUE;
1241 17821123 : pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
1242 : } else {
1243 312362 : if (!pn->isDefn())
1244 153 : return JS_TRUE;
1245 312209 : dn = (Definition *) pn;
1246 : }
1247 :
1248 18133332 : op = pn->getOp();
1249 18133332 : if (op == JSOP_NOP)
1250 0 : return JS_TRUE;
1251 :
1252 18133332 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
1253 18133332 : atom = pn->pn_atom;
1254 18133332 : UpvarCookie cookie = dn->pn_cookie;
1255 18133332 : 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 18133332 : switch (op) {
1267 : case JSOP_NAME:
1268 : case JSOP_SETCONST:
1269 17099792 : 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 1032639 : 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 18133314 : if (dn->isGlobal()) {
1295 182476 : 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 126204 : 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 182476 : cookie.makeFree();
1315 : }
1316 :
1317 18133314 : if (cookie.isFree()) {
1318 12043266 : StackFrame *caller = bce->parser->callerFrame;
1319 12043266 : if (caller) {
1320 25060 : 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 25060 : if (bce->flags & TCF_IN_FOR_INIT)
1327 117 : return JS_TRUE;
1328 :
1329 24943 : 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 24943 : 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 22932 : return JS_TRUE;
1350 : }
1351 :
1352 : /* Optimize accesses to undeclared globals. */
1353 12018206 : if (!TryConvertToGname(bce, pn, &op))
1354 10173206 : return JS_TRUE;
1355 :
1356 : jsatomid _;
1357 1845000 : if (!bce->makeAtomIndex(atom, &_))
1358 0 : return JS_FALSE;
1359 :
1360 1845000 : pn->setOp(op);
1361 1845000 : pn->pn_dflags |= PND_BOUND;
1362 :
1363 1845000 : return JS_TRUE;
1364 : }
1365 :
1366 6090048 : uint16_t level = cookie.level();
1367 6090048 : JS_ASSERT(bce->staticLevel >= level);
1368 :
1369 6090048 : const unsigned skip = bce->staticLevel - level;
1370 6090048 : if (skip != 0) {
1371 636947 : JS_ASSERT(bce->inFunction());
1372 636947 : JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, bce->roLexdeps->lookup(atom));
1373 636947 : 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 636947 : if (op != JSOP_NAME)
1380 37638 : return JS_TRUE;
1381 599309 : if (skip >= UpvarCookie::UPVAR_LEVEL_LIMIT)
1382 0 : return JS_TRUE;
1383 599309 : if (bce->flags & TCF_FUN_HEAVYWEIGHT)
1384 44725 : return JS_TRUE;
1385 :
1386 554584 : if (!bce->fun()->isFlatClosure())
1387 361904 : return JS_TRUE;
1388 :
1389 192680 : if (!bce->upvarIndices.ensureMap(cx))
1390 0 : return JS_FALSE;
1391 :
1392 192680 : AtomIndexAddPtr p = bce->upvarIndices->lookupForAdd(atom);
1393 : jsatomid index;
1394 192680 : if (p) {
1395 80101 : index = p.value();
1396 : } else {
1397 112579 : if (!bce->bindings.addUpvar(cx, atom))
1398 0 : return JS_FALSE;
1399 :
1400 112579 : index = bce->upvarIndices->count();
1401 112579 : if (!bce->upvarIndices->add(p, atom, index))
1402 0 : return JS_FALSE;
1403 :
1404 112579 : UpvarCookies &upvarMap = bce->upvarMap;
1405 : /* upvarMap should have the same number of UpvarCookies as there are lexdeps. */
1406 112579 : size_t lexdepCount = bce->roLexdeps->count();
1407 :
1408 112579 : JS_ASSERT_IF(!upvarMap.empty(), lexdepCount == upvarMap.length());
1409 112579 : if (upvarMap.empty()) {
1410 : /* Lazily initialize the upvar map with exactly the necessary capacity. */
1411 83517 : if (lexdepCount <= upvarMap.sMaxInlineStorage) {
1412 83423 : JS_ALWAYS_TRUE(upvarMap.growByUninitialized(lexdepCount));
1413 : } else {
1414 94 : void *buf = upvarMap.allocPolicy().malloc_(lexdepCount * sizeof(UpvarCookie));
1415 94 : if (!buf)
1416 0 : return JS_FALSE;
1417 94 : upvarMap.replaceRawBuffer(static_cast<UpvarCookie *>(buf), lexdepCount);
1418 : }
1419 289052 : for (size_t i = 0; i < lexdepCount; ++i)
1420 205535 : upvarMap[i] = UpvarCookie();
1421 : }
1422 :
1423 112579 : unsigned slot = cookie.slot();
1424 112579 : if (slot != UpvarCookie::CALLEE_SLOT && dn_kind != Definition::ARG) {
1425 37308 : TreeContext *tc = bce;
1426 37310 : do {
1427 37310 : tc = tc->parent;
1428 : } while (tc->staticLevel != level);
1429 37308 : if (tc->inFunction())
1430 37278 : slot += tc->fun()->nargs;
1431 : }
1432 :
1433 112579 : JS_ASSERT(index < upvarMap.length());
1434 112579 : upvarMap[index].set(skip, slot);
1435 : }
1436 :
1437 192680 : pn->setOp(JSOP_GETFCSLOT);
1438 192680 : JS_ASSERT((index & JS_BITMASK(16)) == index);
1439 192680 : pn->pn_cookie.set(0, index);
1440 192680 : pn->pn_dflags |= PND_BOUND;
1441 192680 : 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 5453101 : switch (dn_kind) {
1450 : case Definition::UNKNOWN:
1451 0 : return JS_TRUE;
1452 :
1453 : case Definition::LET:
1454 956615 : switch (op) {
1455 840286 : case JSOP_NAME: op = JSOP_GETLOCAL; break;
1456 84573 : case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
1457 4561 : case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
1458 25989 : 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 956615 : break;
1464 :
1465 : case Definition::ARG:
1466 2066578 : switch (op) {
1467 2016979 : case JSOP_NAME: op = JSOP_GETARG; break;
1468 48084 : 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 2066578 : JS_ASSERT(!pn->isConst());
1476 2066578 : break;
1477 :
1478 : case Definition::VAR:
1479 2348121 : 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 2428095 : dn_kind == Definition::CONST);
1523 2428095 : switch (op) {
1524 1935798 : case JSOP_NAME: op = JSOP_GETLOCAL; break;
1525 454977 : case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
1526 0 : case JSOP_SETCONST: op = JSOP_SETLOCAL; break;
1527 7972 : case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
1528 28588 : case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
1529 314 : case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
1530 446 : case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
1531 0 : default: JS_NOT_REACHED("local");
1532 : }
1533 2428095 : JS_ASSERT_IF(dn_kind == Definition::CONST, pn->pn_dflags & PND_CONST);
1534 2428095 : break;
1535 : }
1536 :
1537 5451288 : JS_ASSERT(!pn->isOp(op));
1538 5451288 : pn->setOp(op);
1539 5451288 : pn->pn_cookie.set(0, cookie.slot());
1540 5451288 : pn->pn_dflags |= PND_BOUND;
1541 5451288 : return JS_TRUE;
1542 : }
1543 :
1544 : bool
1545 24105 : BytecodeEmitter::addGlobalUse(JSAtom *atom, uint32_t slot, UpvarCookie *cookie)
1546 : {
1547 24105 : if (!globalMap.ensureMap(context()))
1548 0 : return false;
1549 :
1550 24105 : AtomIndexAddPtr p = globalMap->lookupForAdd(atom);
1551 24105 : 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 23754 : 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 23754 : if (!makeAtomIndex(atom, &allAtomIndex))
1566 0 : return false;
1567 :
1568 23754 : jsatomid globalUseIndex = globalUses.length();
1569 23754 : cookie->set(0, globalUseIndex);
1570 :
1571 23754 : GlobalSlotArray::Entry entry = { allAtomIndex, slot };
1572 23754 : if (!globalUses.append(entry))
1573 0 : return false;
1574 :
1575 23754 : 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 6573247 : CheckSideEffects(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool *answer)
1592 : {
1593 : JSBool ok;
1594 : ParseNode *pn2;
1595 :
1596 6573247 : ok = JS_TRUE;
1597 6573247 : if (!pn || *answer)
1598 153966 : return ok;
1599 :
1600 6419281 : 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 37769 : *answer = JS_FALSE;
1610 37769 : break;
1611 :
1612 : case PN_LIST:
1613 5591631 : if (pn->isOp(JSOP_NOP) || pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) ||
1614 3726196 : pn->isOp(JSOP_STRICTEQ) || pn->isOp(JSOP_STRICTNE)) {
1615 : /*
1616 : * Non-operators along with ||, &&, ===, and !== never invoke
1617 : * toString or valueOf.
1618 : */
1619 5608 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
1620 3271 : 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 1863098 : *answer = JS_TRUE;
1639 : }
1640 1865435 : break;
1641 :
1642 : case PN_TERNARY:
1643 6269 : ok = CheckSideEffects(cx, bce, pn->pn_kid1, answer) &&
1644 6269 : CheckSideEffects(cx, bce, pn->pn_kid2, answer) &&
1645 12538 : CheckSideEffects(cx, bce, pn->pn_kid3, answer);
1646 6269 : break;
1647 :
1648 : case PN_BINARY:
1649 2431039 : 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 2372632 : pn2 = pn->pn_left;
1660 2372632 : if (!pn2->isKind(PNK_NAME)) {
1661 1741460 : *answer = JS_TRUE;
1662 : } else {
1663 631172 : if (!BindNameToSlot(cx, bce, pn2))
1664 0 : return JS_FALSE;
1665 631172 : if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
1666 0 : return JS_FALSE;
1667 631172 : if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
1668 375330 : *answer = JS_TRUE;
1669 : }
1670 : } else {
1671 108820 : if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) ||
1672 50413 : pn->isOp(JSOP_STRICTNE)) {
1673 : /*
1674 : * ||, &&, ===, and !== do not convert their operands via
1675 : * toString or valueOf method calls.
1676 : */
1677 8021 : ok = CheckSideEffects(cx, bce, pn->pn_left, answer) &&
1678 8021 : 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 50386 : *answer = JS_TRUE;
1685 : }
1686 : }
1687 2431039 : break;
1688 :
1689 : case PN_UNARY:
1690 1662302 : switch (pn->getKind()) {
1691 : case PNK_DELETE:
1692 37365 : pn2 = pn->pn_kid;
1693 37365 : 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 23300 : 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 37365 : *answer = JS_TRUE;
1713 37365 : break;
1714 : default:
1715 0 : ok = CheckSideEffects(cx, bce, pn2, answer);
1716 0 : break;
1717 : }
1718 37365 : 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 1621532 : *answer = JS_TRUE;
1739 1621532 : break;
1740 : }
1741 1662302 : 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 138994 : if (pn->isKind(PNK_NAME) && !pn->isOp(JSOP_NOP)) {
1750 76207 : if (!BindNameToSlot(cx, bce, pn))
1751 0 : return JS_FALSE;
1752 152297 : if (!pn->isOp(JSOP_ARGUMENTS) && !pn->isOp(JSOP_CALLEE) &&
1753 76090 : 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 6136 : *answer = JS_TRUE;
1760 : }
1761 : }
1762 138994 : pn2 = pn->maybeExpr();
1763 138994 : if (pn->isKind(PNK_DOT)) {
1764 62787 : if (pn2->isKind(PNK_NAME) && !BindNameToSlot(cx, bce, pn2))
1765 0 : return JS_FALSE;
1766 125565 : 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 62778 : *answer = JS_TRUE;
1773 : }
1774 : }
1775 138994 : ok = CheckSideEffects(cx, bce, pn2, answer);
1776 138994 : 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 277473 : if (pn->isKind(PNK_DEBUGGER))
1784 0 : *answer = JS_TRUE;
1785 277473 : break;
1786 : }
1787 6419281 : return ok;
1788 : }
1789 :
1790 : bool
1791 671752 : BytecodeEmitter::needsImplicitThis()
1792 : {
1793 671752 : if (!compileAndGo())
1794 666329 : 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 13058584 : EmitNameOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool callContext)
1816 : {
1817 : JSOp op;
1818 :
1819 13058584 : if (!BindNameToSlot(cx, bce, pn))
1820 0 : return JS_FALSE;
1821 13058584 : op = pn->getOp();
1822 :
1823 13058584 : if (callContext) {
1824 858336 : switch (op) {
1825 : case JSOP_NAME:
1826 671752 : op = JSOP_CALLNAME;
1827 671752 : break;
1828 : case JSOP_GETGNAME:
1829 102648 : op = JSOP_CALLGNAME;
1830 102648 : break;
1831 : case JSOP_GETARG:
1832 17057 : op = JSOP_CALLARG;
1833 17057 : break;
1834 : case JSOP_GETLOCAL:
1835 44897 : op = JSOP_CALLLOCAL;
1836 44897 : break;
1837 : case JSOP_GETFCSLOT:
1838 21531 : op = JSOP_CALLFCSLOT;
1839 21531 : break;
1840 : default:
1841 451 : JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE);
1842 451 : break;
1843 : }
1844 : }
1845 :
1846 13058584 : if (op == JSOP_ARGUMENTS) {
1847 13766 : if (!EmitArguments(cx, bce))
1848 0 : return JS_FALSE;
1849 13044818 : } else if (op == JSOP_CALLEE) {
1850 1774 : if (Emit1(cx, bce, op) < 0)
1851 0 : return JS_FALSE;
1852 : } else {
1853 13043044 : if (!pn->pn_cookie.isFree()) {
1854 4986144 : JS_ASSERT(JOF_OPTYPE(op) != JOF_ATOM);
1855 4986144 : EMIT_UINT16_IMM_OP(op, pn->pn_cookie.slot());
1856 : } else {
1857 8056900 : if (!EmitAtomOp(cx, pn, op, bce))
1858 0 : return JS_FALSE;
1859 : }
1860 : }
1861 :
1862 : /* Need to provide |this| value for call */
1863 13058584 : if (callContext) {
1864 858336 : if (op == JSOP_CALLNAME && bce->needsImplicitThis()) {
1865 666536 : if (!EmitAtomOp(cx, pn, JSOP_IMPLICITTHIS, bce))
1866 0 : return false;
1867 : } else {
1868 191800 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
1869 0 : return false;
1870 : }
1871 : }
1872 :
1873 13058584 : 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 5227747 : EmitElemOpBase(JSContext *cx, BytecodeEmitter *bce, JSOp op)
1903 : {
1904 5227747 : if (Emit1(cx, bce, op) < 0)
1905 0 : return false;
1906 5227747 : CheckTypeSet(cx, bce, op);
1907 5227747 : if (op == JSOP_CALLELEM)
1908 4711 : return Emit1(cx, bce, JSOP_SWAP) >= 0;
1909 5223036 : 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 8164254 : 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 8164254 : JS_ASSERT(pn->isArity(PN_NAME));
1940 8164254 : pn2 = pn->maybeExpr();
1941 :
1942 : /* Special case deoptimization for __proto__. */
1943 8164254 : 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 8163927 : if (callContext) {
1951 2293600 : JS_ASSERT(pn->isKind(PNK_DOT));
1952 2293600 : JS_ASSERT(op == JSOP_GETPROP);
1953 2293600 : op = JSOP_CALLPROP;
1954 5870327 : } else if (op == JSOP_GETPROP && pn->isKind(PNK_DOT)) {
1955 5842592 : if (pn2->isKind(PNK_NAME)) {
1956 4844673 : 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 8163927 : if (pn2->isKind(PNK_DOT)) {
1967 1033206 : pndot = pn2;
1968 1033206 : pnup = NULL;
1969 1033206 : top = bce->offset();
1970 53700 : for (;;) {
1971 : /* Reverse pndot->pn_expr to point up, not down. */
1972 1086906 : pndot->pn_offset = top;
1973 1086906 : JS_ASSERT(!pndot->isUsed());
1974 1086906 : pndown = pndot->pn_expr;
1975 1086906 : pndot->pn_expr = pnup;
1976 1086906 : if (!pndown->isKind(PNK_DOT))
1977 : break;
1978 53700 : pnup = pndot;
1979 53700 : pndot = pndown;
1980 : }
1981 :
1982 : /* pndown is a primary expression, not a dotted property reference. */
1983 1033206 : if (!EmitTree(cx, bce, pndown))
1984 0 : return false;
1985 :
1986 1086906 : do {
1987 : /* Walk back up the list, emitting annotated name ops. */
1988 1086906 : 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 1086906 : 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 1086845 : } else if (!EmitAtomOp(cx, pndot, pndot->getOp(), bce)) {
1996 0 : return false;
1997 : }
1998 :
1999 : /* Reverse the pn_expr link again. */
2000 1086906 : pnup = pndot->pn_expr;
2001 1086906 : pndot->pn_expr = pndown;
2002 1086906 : pndown = pndot;
2003 : } while ((pndot = pnup) != NULL);
2004 : } else {
2005 7130721 : if (!EmitTree(cx, bce, pn2))
2006 0 : return false;
2007 : }
2008 :
2009 8163927 : if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_DUP) < 0)
2010 0 : return false;
2011 :
2012 8163927 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
2013 0 : return false;
2014 :
2015 8163927 : if (!EmitAtomOp(cx, pn, op, bce))
2016 0 : return false;
2017 :
2018 8163927 : if (op == JSOP_CALLPROP && Emit1(cx, bce, JSOP_SWAP) < 0)
2019 0 : return false;
2020 :
2021 8163927 : return true;
2022 : }
2023 :
2024 : static bool
2025 4863 : EmitPropIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2026 : {
2027 4863 : 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 4863 : int start = bce->offset();
2035 :
2036 4863 : const JSCodeSpec *cs = &js_CodeSpec[op];
2037 4863 : JS_ASSERT(cs->format & JOF_PROP);
2038 4863 : JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
2039 :
2040 4863 : bool post = (cs->format & JOF_POST);
2041 4863 : JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
2042 :
2043 : // OBJ
2044 4863 : if (Emit1(cx, bce, JSOP_DUP) < 0) // OBJ OBJ
2045 0 : return false;
2046 4863 : if (!EmitAtomOp(cx, pn, JSOP_GETPROP, bce)) // OBJ V
2047 0 : return false;
2048 4863 : if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ N
2049 0 : return false;
2050 4863 : if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ N? N
2051 0 : return false;
2052 4863 : if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ N? N 1
2053 0 : return false;
2054 4863 : if (Emit1(cx, bce, binop) < 0) // OBJ N? N+1
2055 0 : return false;
2056 :
2057 4863 : if (post) {
2058 4383 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
2059 0 : return false;
2060 4383 : if (Emit1(cx, bce, JSOP_SWAP) < 0) // N? OBJ N+1
2061 0 : return false;
2062 : }
2063 :
2064 4863 : if (!EmitAtomOp(cx, pn, JSOP_SETPROP, bce)) // N? N+1
2065 0 : return false;
2066 4863 : if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
2067 0 : return false;
2068 :
2069 4863 : UpdateDecomposeLength(bce, start);
2070 :
2071 4863 : return true;
2072 : }
2073 :
2074 : static bool
2075 22603 : EmitNameIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2076 : {
2077 : /* Emit the composite op, including the slack byte at the end. */
2078 22603 : 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 22603 : bce->stackDepth--;
2083 :
2084 22603 : int start = bce->offset();
2085 :
2086 22603 : const JSCodeSpec *cs = &js_CodeSpec[op];
2087 22603 : JS_ASSERT((cs->format & JOF_NAME) || (cs->format & JOF_GNAME));
2088 22603 : JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
2089 :
2090 22603 : bool global = (cs->format & JOF_GNAME);
2091 22603 : bool post = (cs->format & JOF_POST);
2092 22603 : JSOp binop = (cs->format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
2093 :
2094 22603 : if (!EmitAtomOp(cx, pn, global ? JSOP_BINDGNAME : JSOP_BINDNAME, bce)) // OBJ
2095 0 : return false;
2096 22603 : if (!EmitAtomOp(cx, pn, global ? JSOP_GETGNAME : JSOP_NAME, bce)) // OBJ V
2097 0 : return false;
2098 22603 : if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ N
2099 0 : return false;
2100 22603 : if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ N? N
2101 0 : return false;
2102 22603 : if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ N? N 1
2103 0 : return false;
2104 22603 : if (Emit1(cx, bce, binop) < 0) // OBJ N? N+1
2105 0 : return false;
2106 :
2107 22603 : if (post) {
2108 9160 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N? N+1 OBJ
2109 0 : return false;
2110 9160 : if (Emit1(cx, bce, JSOP_SWAP) < 0) // N? OBJ N+1
2111 0 : return false;
2112 : }
2113 :
2114 22603 : if (!EmitAtomOp(cx, pn, global ? JSOP_SETGNAME : JSOP_SETNAME, bce)) // N? N+1
2115 0 : return false;
2116 22603 : if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
2117 0 : return false;
2118 :
2119 22603 : UpdateDecomposeLength(bce, start);
2120 :
2121 22603 : return true;
2122 : }
2123 :
2124 : static JSBool
2125 1926276 : EmitElemOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2126 : {
2127 : ParseNode *left, *right;
2128 :
2129 1926276 : ptrdiff_t top = bce->offset();
2130 :
2131 1926276 : 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 1925071 : JS_ASSERT(pn->isArity(PN_BINARY));
2156 1925071 : left = pn->pn_left;
2157 1925071 : right = pn->pn_right;
2158 : }
2159 :
2160 1926276 : if (op == JSOP_GETELEM && left->isKind(PNK_NAME) && right->isKind(PNK_NUMBER)) {
2161 36621 : if (!BindNameToSlot(cx, bce, left))
2162 0 : return false;
2163 : }
2164 :
2165 1926276 : if (!EmitTree(cx, bce, left))
2166 0 : return false;
2167 :
2168 1926276 : 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 1926294 : right->isOp(JSOP_QNAMEPART));
2174 1926276 : if (!EmitTree(cx, bce, right))
2175 0 : return false;
2176 1926276 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - top) < 0)
2177 0 : return false;
2178 1926276 : return EmitElemOpBase(cx, bce, op);
2179 : }
2180 :
2181 : static bool
2182 1634121 : EmitElemIncDec(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
2183 : {
2184 1634121 : if (pn) {
2185 1634112 : 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 1634121 : 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 1634121 : bce->stackDepth++;
2196 :
2197 1634121 : int start = bce->offset();
2198 :
2199 1634121 : const JSCodeSpec *cs = &js_CodeSpec[op];
2200 1634121 : JS_ASSERT(cs->format & JOF_ELEM);
2201 1634121 : JS_ASSERT(cs->format & (JOF_INC | JOF_DEC));
2202 :
2203 1634121 : bool post = (cs->format & JOF_POST);
2204 1634121 : 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 1634121 : if (Emit1(cx, bce, JSOP_TOID) < 0) // OBJ KEY
2212 0 : return false;
2213 1634121 : if (Emit1(cx, bce, JSOP_DUP2) < 0) // OBJ KEY OBJ KEY
2214 0 : return false;
2215 1634121 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM)) // OBJ KEY V
2216 0 : return false;
2217 1634121 : if (Emit1(cx, bce, JSOP_POS) < 0) // OBJ KEY N
2218 0 : return false;
2219 1634121 : if (post && Emit1(cx, bce, JSOP_DUP) < 0) // OBJ KEY N? N
2220 0 : return false;
2221 1634121 : if (Emit1(cx, bce, JSOP_ONE) < 0) // OBJ KEY N? N 1
2222 0 : return false;
2223 1634121 : if (Emit1(cx, bce, binop) < 0) // OBJ KEY N? N+1
2224 0 : return false;
2225 :
2226 1634121 : if (post) {
2227 1634004 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // KEY N N+1 OBJ
2228 0 : return false;
2229 1634004 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)3) < 0) // N N+1 OBJ KEY
2230 0 : return false;
2231 1634004 : if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)2) < 0) // N OBJ KEY N+1
2232 0 : return false;
2233 : }
2234 :
2235 1634121 : if (!EmitElemOpBase(cx, bce, JSOP_SETELEM)) // N? N+1
2236 0 : return false;
2237 1634121 : if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
2238 0 : return false;
2239 :
2240 1634121 : UpdateDecomposeLength(bce, start);
2241 :
2242 1634121 : return true;
2243 : }
2244 :
2245 : static JSBool
2246 11182990 : 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 11182990 : if (JSDOUBLE_IS_INT32(dval, &ival)) {
2254 11171522 : if (ival == 0)
2255 2125656 : return Emit1(cx, bce, JSOP_ZERO) >= 0;
2256 9045866 : if (ival == 1)
2257 298355 : return Emit1(cx, bce, JSOP_ONE) >= 0;
2258 8747511 : if ((int)(int8_t)ival == ival)
2259 1468029 : return Emit2(cx, bce, JSOP_INT8, (jsbytecode)(int8_t)ival) >= 0;
2260 :
2261 7279482 : u = (uint32_t)ival;
2262 7279482 : if (u < JS_BIT(16)) {
2263 7269987 : EMIT_UINT16_IMM_OP(JSOP_UINT16, u);
2264 9495 : } else if (u < JS_BIT(24)) {
2265 3389 : off = EmitN(cx, bce, JSOP_UINT24, 3);
2266 3389 : if (off < 0)
2267 0 : return JS_FALSE;
2268 3389 : pc = bce->code(off);
2269 3389 : SET_UINT24(pc, u);
2270 : } else {
2271 6106 : off = EmitN(cx, bce, JSOP_INT32, 4);
2272 6106 : if (off < 0)
2273 0 : return JS_FALSE;
2274 6106 : pc = bce->code(off);
2275 6106 : SET_INT32(pc, ival);
2276 : }
2277 7279482 : return JS_TRUE;
2278 : }
2279 :
2280 11468 : if (!bce->constList.append(DoubleValue(dval)))
2281 0 : return JS_FALSE;
2282 :
2283 11468 : 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 48023 : AllocateSwitchConstant(JSContext *cx)
2296 : {
2297 48023 : return cx->tempLifoAlloc().new_<Value>();
2298 : }
2299 :
2300 : static inline void
2301 1284510 : SetJumpOffsetAt(BytecodeEmitter *bce, ptrdiff_t off)
2302 : {
2303 1284510 : SET_JUMP_OFFSET(bce->code(off), bce->offset() - off);
2304 1284510 : }
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 17196 : 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 17196 : switchOp = JSOP_TABLESWITCH;
2328 17196 : ok = JS_TRUE;
2329 17196 : hasDefault = constPropagated = JS_FALSE;
2330 17196 : defaultOffset = -1;
2331 :
2332 17196 : 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 17196 : uint32_t blockObjCount = 0;
2339 17196 : if (pn2->isKind(PNK_LEXICALSCOPE)) {
2340 3517 : blockObjCount = pn2->pn_objbox->object->asStaticBlock().slotCount();
2341 13946 : for (uint32_t i = 0; i < blockObjCount; ++i) {
2342 10429 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
2343 0 : return JS_FALSE;
2344 : }
2345 : }
2346 : #endif
2347 :
2348 : /* Push the discriminant. */
2349 17196 : if (!EmitTree(cx, bce, pn->pn_left))
2350 0 : return JS_FALSE;
2351 :
2352 : #if JS_HAS_BLOCK_SCOPE
2353 17196 : if (pn2->isKind(PNK_LEXICALSCOPE)) {
2354 3517 : PushBlockScope(bce, &stmtInfo, pn2->pn_objbox->object->asStaticBlock(), -1);
2355 3517 : stmtInfo.type = STMT_SWITCH;
2356 3517 : 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 17196 : top = bce->offset();
2363 : #if !JS_HAS_BLOCK_SCOPE
2364 : PushStatement(bce, &stmtInfo, STMT_SWITCH, top);
2365 : #else
2366 17196 : if (pn2->isKind(PNK_STATEMENTLIST)) {
2367 13679 : 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 3517 : stmtInfo.update = top = bce->offset();
2374 :
2375 : /* Advance pn2 to refer to the switch case list. */
2376 3517 : pn2 = pn2->expr();
2377 : }
2378 : #endif
2379 :
2380 17196 : caseCount = pn2->pn_count;
2381 17196 : tableLength = 0;
2382 17196 : table = NULL;
2383 :
2384 17370 : if (caseCount == 0 ||
2385 : (caseCount == 1 &&
2386 174 : (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 17115 : jsbitmap *intmap = NULL;
2394 17115 : int32_t intmap_bitlen = 0;
2395 :
2396 17115 : low = JSVAL_INT_MAX;
2397 17115 : high = JSVAL_INT_MIN;
2398 :
2399 101490 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2400 84375 : if (pn3->isKind(PNK_DEFAULT)) {
2401 9447 : hasDefault = JS_TRUE;
2402 9447 : caseCount--; /* one of the "cases" was the default */
2403 9447 : continue;
2404 : }
2405 :
2406 74928 : JS_ASSERT(pn3->isKind(PNK_CASE));
2407 74928 : if (switchOp == JSOP_CONDSWITCH)
2408 19897 : continue;
2409 :
2410 55031 : pn4 = pn3->pn_left;
2411 110062 : while (pn4->isKind(PNK_RP))
2412 0 : pn4 = pn4->pn_kid;
2413 :
2414 : Value constVal;
2415 55031 : switch (pn4->getKind()) {
2416 : case PNK_NUMBER:
2417 4958 : constVal.setNumber(pn4->pn_dval);
2418 4958 : break;
2419 : case PNK_STRING:
2420 42934 : constVal.setString(pn4->pn_atom);
2421 42934 : 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 2356 : if (!pn4->maybeExpr()) {
2433 2356 : ok = LookupCompileTimeConstant(cx, bce, pn4->pn_atom, &constVal);
2434 2356 : if (!ok)
2435 0 : goto release;
2436 2356 : 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 7008 : switchOp = JSOP_CONDSWITCH;
2452 7008 : continue;
2453 : }
2454 48023 : JS_ASSERT(constVal.isPrimitive());
2455 :
2456 48023 : pn3->pn_pval = AllocateSwitchConstant(cx);
2457 48023 : if (!pn3->pn_pval) {
2458 0 : ok = JS_FALSE;
2459 0 : goto release;
2460 : }
2461 :
2462 48023 : *pn3->pn_pval = constVal;
2463 :
2464 48023 : if (switchOp != JSOP_TABLESWITCH)
2465 34196 : continue;
2466 13827 : if (!pn3->pn_pval->isInt32()) {
2467 8851 : switchOp = JSOP_LOOKUPSWITCH;
2468 8851 : 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 17115 : if (intmap && intmap != intmap_space)
2514 28 : cx->free_(intmap);
2515 17115 : 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 17115 : 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 15728 : } 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 8720 : 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 17196 : noteIndex = NewSrcNote3(cx, bce, SRC_SWITCH, 0, 0);
2543 17196 : if (noteIndex < 0)
2544 0 : return JS_FALSE;
2545 :
2546 17196 : if (switchOp == JSOP_CONDSWITCH) {
2547 : /*
2548 : * 0 bytes of immediate for unoptimized ECMAv2 switch.
2549 : */
2550 7008 : switchSize = 0;
2551 10188 : } 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 8880 : (UINT32_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount);
2564 : }
2565 :
2566 : /* Emit switchOp followed by switchSize bytes of jump or lookup table. */
2567 17196 : if (EmitN(cx, bce, switchOp, switchSize) < 0)
2568 0 : return JS_FALSE;
2569 :
2570 17196 : off = -1;
2571 17196 : if (switchOp == JSOP_CONDSWITCH) {
2572 7008 : int caseNoteIndex = -1;
2573 7008 : JSBool beforeCases = JS_TRUE;
2574 :
2575 : /* Emit code for evaluating cases and jumping to case statements. */
2576 37843 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2577 30835 : pn4 = pn3->pn_left;
2578 30835 : if (pn4 && !EmitTree(cx, bce, pn4))
2579 0 : return JS_FALSE;
2580 30835 : if (caseNoteIndex >= 0) {
2581 : /* off is the previous JSOP_CASE's bytecode offset. */
2582 23440 : if (!SetSrcNoteOffset(cx, bce, (unsigned)caseNoteIndex, 0, bce->offset() - off))
2583 0 : return JS_FALSE;
2584 : }
2585 30835 : if (!pn4) {
2586 3772 : JS_ASSERT(pn3->isKind(PNK_DEFAULT));
2587 3772 : continue;
2588 : }
2589 27063 : caseNoteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
2590 27063 : if (caseNoteIndex < 0)
2591 0 : return JS_FALSE;
2592 27063 : off = EmitJump(cx, bce, JSOP_CASE, 0);
2593 27063 : if (off < 0)
2594 0 : return JS_FALSE;
2595 27063 : pn3->pn_offset = off;
2596 27063 : if (beforeCases) {
2597 : unsigned noteCount, noteCountDelta;
2598 :
2599 : /* Switch note's second offset is to first JSOP_CASE. */
2600 7008 : noteCount = bce->noteCount();
2601 7008 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, off - top))
2602 0 : return JS_FALSE;
2603 7008 : noteCountDelta = bce->noteCount() - noteCount;
2604 7008 : if (noteCountDelta != 0)
2605 0 : caseNoteIndex += noteCountDelta;
2606 7008 : 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 10244 : if (!hasDefault &&
2617 : caseNoteIndex >= 0 &&
2618 3236 : !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 7008 : defaultOffset = EmitJump(cx, bce, JSOP_DEFAULT, 0);
2625 7008 : if (defaultOffset < 0)
2626 0 : return JS_FALSE;
2627 : } else {
2628 10188 : pc = bce->code(top + JUMP_OFFSET_LEN);
2629 :
2630 10188 : 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 8880 : JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH);
2659 :
2660 : /* Fill in the number of cases. */
2661 8880 : SET_UINT16(pc, caseCount);
2662 8880 : 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 10188 : 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 101607 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2720 84411 : if (switchOp == JSOP_CONDSWITCH && !pn3->isKind(PNK_DEFAULT))
2721 27063 : SetJumpOffsetAt(bce, pn3->pn_offset);
2722 84411 : pn4 = pn3->pn_right;
2723 84411 : ok = EmitTree(cx, bce, pn4);
2724 84411 : if (!ok)
2725 0 : goto out;
2726 84411 : pn3->pn_offset = pn4->pn_offset;
2727 84411 : if (pn3->isKind(PNK_DEFAULT))
2728 9483 : off = pn3->pn_offset - top;
2729 : }
2730 :
2731 17196 : if (!hasDefault) {
2732 : /* If no default case, offset for default is to end of switch. */
2733 7713 : off = bce->offset() - top;
2734 : }
2735 :
2736 : /* We better have set "off" by now. */
2737 17196 : JS_ASSERT(off != -1);
2738 :
2739 : /* Set the default offset (to end of switch if no default). */
2740 17196 : if (switchOp == JSOP_CONDSWITCH) {
2741 7008 : pc = NULL;
2742 7008 : JS_ASSERT(defaultOffset != -1);
2743 7008 : SET_JUMP_OFFSET(bce->code(defaultOffset), off - (defaultOffset - top));
2744 : } else {
2745 10188 : pc = bce->code(top);
2746 10188 : SET_JUMP_OFFSET(pc, off);
2747 10188 : pc += JUMP_OFFSET_LEN;
2748 : }
2749 :
2750 : /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
2751 17196 : off = bce->offset() - top;
2752 17196 : ok = SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, off);
2753 17196 : if (!ok)
2754 0 : goto out;
2755 :
2756 17196 : 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 15888 : } else if (switchOp == JSOP_LOOKUPSWITCH) {
2768 : /* Skip over the already-initialized number of cases. */
2769 8880 : pc += UINT16_LEN;
2770 :
2771 57264 : for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
2772 48384 : if (pn3->isKind(PNK_DEFAULT))
2773 4644 : continue;
2774 43740 : if (!bce->constList.append(*pn3->pn_pval))
2775 0 : goto bad;
2776 43740 : SET_UINT32_INDEX(pc, bce->constList.length() - 1);
2777 43740 : pc += UINT32_INDEX_LEN;
2778 :
2779 43740 : off = pn3->pn_offset - top;
2780 43740 : SET_JUMP_OFFSET(pc, off);
2781 43740 : pc += JUMP_OFFSET_LEN;
2782 : }
2783 : }
2784 :
2785 : out:
2786 17196 : if (table)
2787 1227 : cx->free_(table);
2788 17196 : if (ok) {
2789 17196 : ok = PopStatementBCE(cx, bce);
2790 :
2791 : #if JS_HAS_BLOCK_SCOPE
2792 17196 : if (ok && pn->pn_right->isKind(PNK_LEXICALSCOPE))
2793 3517 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObjCount);
2794 : #endif
2795 : }
2796 17196 : return ok;
2797 :
2798 : bad:
2799 0 : ok = JS_FALSE;
2800 0 : goto out;
2801 : }
2802 :
2803 : JSBool
2804 986588 : 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 986588 : if (bce->flags & TCF_FUN_IS_GENERATOR) {
2814 : /* JSOP_GENERATOR must be the first instruction. */
2815 3694 : bce->switchToProlog();
2816 3694 : JS_ASSERT(bce->next() == bce->base());
2817 3694 : if (Emit1(cx, bce, JSOP_GENERATOR) < 0)
2818 0 : return false;
2819 3694 : 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 986588 : 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 986588 : return EmitTree(cx, bce, body) &&
2837 986588 : Emit1(cx, bce, JSOP_STOP) >= 0 &&
2838 1973176 : JSScript::NewScriptFromEmitter(cx, bce);
2839 : }
2840 :
2841 : static bool
2842 1654476 : MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
2843 : jsatomid *result)
2844 : {
2845 : jsatomid atomIndex;
2846 :
2847 1654476 : if (!pn->pn_cookie.isFree()) {
2848 1341727 : atomIndex = pn->pn_cookie.slot();
2849 : } else {
2850 312749 : if (!bce->makeAtomIndex(pn->pn_atom, &atomIndex))
2851 0 : return false;
2852 : }
2853 :
2854 2279974 : if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
2855 312749 : (!bce->inFunction() || (bce->flags & TCF_FUN_HEAVYWEIGHT)) &&
2856 312749 : !(pn->pn_dflags & PND_GVAR))
2857 : {
2858 266790 : bce->switchToProlog();
2859 266790 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
2860 0 : return false;
2861 266790 : if (!EmitIndexOp(cx, prologOp, atomIndex, bce))
2862 0 : return false;
2863 266790 : bce->switchToMain();
2864 : }
2865 :
2866 4893452 : if (bce->inFunction() &&
2867 1185440 : JOF_OPTYPE(pn->getOp()) == JOF_LOCAL &&
2868 1184803 : pn->pn_cookie.slot() < bce->bindings.countVars() &&
2869 868733 : bce->shouldNoteClosedName(pn))
2870 : {
2871 80227 : if (!bce->closedVars.append(pn->pn_cookie.slot()))
2872 0 : return false;
2873 : }
2874 :
2875 1654476 : if (result)
2876 1628548 : *result = atomIndex;
2877 1654476 : 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 25928 : EmitDestructuringDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
2902 : {
2903 25928 : JS_ASSERT(pn->isKind(PNK_NAME));
2904 25928 : if (!BindNameToSlot(cx, bce, pn))
2905 0 : return JS_FALSE;
2906 :
2907 25928 : JS_ASSERT(!pn->isOp(JSOP_ARGUMENTS) && !pn->isOp(JSOP_CALLEE));
2908 25928 : return MaybeEmitVarDecl(cx, bce, prologOp, pn, NULL);
2909 : }
2910 :
2911 : static JSBool
2912 18766 : EmitDestructuringDecls(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn)
2913 : {
2914 : ParseNode *pn2, *pn3;
2915 : DestructuringDeclEmitter emitter;
2916 :
2917 18766 : if (pn->isKind(PNK_RB)) {
2918 45282 : for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
2919 27632 : if (pn2->isKind(PNK_COMMA))
2920 691 : continue;
2921 26941 : emitter = (pn2->isKind(PNK_NAME))
2922 : ? EmitDestructuringDecl
2923 26941 : : EmitDestructuringDecls;
2924 26941 : 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 18766 : 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 34544 : EmitDestructuringLHS(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption)
2957 : {
2958 34544 : 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 34544 : 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 31352 : 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 30452 : if (pn->isKind(PNK_NAME)) {
2991 30326 : if (!BindNameToSlot(cx, bce, pn))
2992 0 : return JS_FALSE;
2993 30326 : if (pn->isConst() && !pn->isInitialized())
2994 0 : return Emit1(cx, bce, JSOP_POP) >= 0;
2995 : }
2996 :
2997 30452 : 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 28604 : uint16_t slot = pn->pn_cookie.slot();
3017 28604 : EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot);
3018 28604 : 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 33644 : 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 21502 : EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
3067 : VarEmitOption emitOption)
3068 : {
3069 21502 : JS_ASSERT(emitOption != DefineVars);
3070 :
3071 : unsigned index;
3072 : ParseNode *pn2, *pn3;
3073 : JSBool doElemOp;
3074 :
3075 : #ifdef DEBUG
3076 21502 : int stackDepth = bce->stackDepth;
3077 21502 : JS_ASSERT(stackDepth != 0);
3078 21502 : JS_ASSERT(pn->isArity(PN_LIST));
3079 21502 : JS_ASSERT(pn->isKind(PNK_RB) || pn->isKind(PNK_RC));
3080 : #endif
3081 :
3082 21502 : 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 21502 : index = 0;
3089 55437 : 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 33935 : if (pn2 != pn->pn_head && NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
3095 0 : return JS_FALSE;
3096 33935 : 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 33935 : doElemOp = JS_TRUE;
3106 33935 : if (pn->isKind(PNK_RB)) {
3107 31846 : if (!EmitNumberOp(cx, index, bce))
3108 0 : return JS_FALSE;
3109 31846 : 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 33935 : 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 31954 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
3140 0 : return JS_FALSE;
3141 31954 : JS_ASSERT(bce->stackDepth >= stackDepth + 1);
3142 : }
3143 :
3144 : /* Nullary comma node makes a hole in the array destructurer. */
3145 33935 : 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 33235 : int depthBefore = bce->stackDepth;
3152 33235 : if (!EmitDestructuringLHS(cx, bce, pn3, emitOption))
3153 0 : return JS_FALSE;
3154 :
3155 33235 : 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 33935 : ++index;
3181 : }
3182 :
3183 21502 : 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 21502 : return JS_TRUE;
3193 : }
3194 :
3195 : static ptrdiff_t
3196 10211 : OpToDeclType(JSOp op)
3197 : {
3198 10211 : switch (op) {
3199 : case JSOP_NOP:
3200 4249 : 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 45060 : LetNotes(JSContext *cx) : notes(cx), groupAssign(false), updateCalled(false) {}
3230 :
3231 90120 : ~LetNotes() {
3232 45060 : JS_ASSERT_IF(!notes.allocPolicy().context()->isExceptionPending(), updateCalled);
3233 45060 : }
3234 :
3235 180 : void setGroupAssign() {
3236 180 : JS_ASSERT(notes.empty());
3237 180 : groupAssign = true;
3238 180 : }
3239 :
3240 45060 : bool isGroupAssign() const {
3241 45060 : 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 45060 : bool update(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t offset) {
3262 45060 : JS_ASSERT(!updateCalled);
3263 49218 : 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 45060 : updateCalled = true;
3271 45060 : return true;
3272 : }
3273 : };
3274 :
3275 : static JSBool
3276 18310 : 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 18310 : 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 14152 : 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 18310 : VarEmitOption emitOption = letNotes ? PushInitialValues : InitializeVars;
3299 18310 : return EmitDestructuringOpsHelper(cx, bce, pn, emitOption);
3300 : }
3301 :
3302 : static JSBool
3303 674 : EmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp,
3304 : ParseNode *lhs, ParseNode *rhs)
3305 : {
3306 : unsigned depth, limit, i, nslots;
3307 : ParseNode *pn;
3308 :
3309 674 : depth = limit = (unsigned) bce->stackDepth;
3310 1983 : for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
3311 1309 : 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 1309 : JS_ASSERT(!(pn->isKind(PNK_COMMA) && pn->isArity(PN_NULLARY)));
3319 1309 : if (!EmitTree(cx, bce, pn))
3320 0 : return JS_FALSE;
3321 1309 : ++limit;
3322 : }
3323 :
3324 674 : if (NewSrcNote2(cx, bce, SRC_GROUPASSIGN, OpToDeclType(prologOp)) < 0)
3325 0 : return JS_FALSE;
3326 :
3327 674 : i = depth;
3328 1983 : for (pn = lhs->pn_head; pn; pn = pn->pn_next, ++i) {
3329 : /* MaybeEmitGroupAssignment requires lhs->pn_count <= rhs->pn_count. */
3330 1309 : JS_ASSERT(i < limit);
3331 1309 : int slot = AdjustBlockSlot(cx, bce, i);
3332 1309 : if (slot < 0)
3333 0 : return JS_FALSE;
3334 1309 : EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot);
3335 :
3336 1309 : 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 1309 : if (!EmitDestructuringLHS(cx, bce, pn, InitializeVars))
3341 0 : return JS_FALSE;
3342 : }
3343 : }
3344 :
3345 674 : nslots = limit - depth;
3346 674 : EMIT_UINT16_IMM_OP(JSOP_POPN, nslots);
3347 674 : bce->stackDepth = (unsigned) depth;
3348 674 : 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 2303314 : MaybeEmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn,
3358 : JSOp *pop)
3359 : {
3360 2303314 : JS_ASSERT(pn->isKind(PNK_ASSIGN));
3361 2303314 : JS_ASSERT(pn->isOp(JSOP_NOP));
3362 2303314 : JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
3363 :
3364 2303314 : ParseNode *lhs = pn->pn_left;
3365 2303314 : ParseNode *rhs = pn->pn_right;
3366 2303988 : if (lhs->isKind(PNK_RB) && rhs->isKind(PNK_RB) &&
3367 674 : !(rhs->pn_xflags & PNX_HOLEY) &&
3368 : lhs->pn_count <= rhs->pn_count) {
3369 674 : if (!EmitGroupAssignment(cx, bce, prologOp, lhs, rhs))
3370 0 : return JS_FALSE;
3371 674 : *pop = JSOP_NOP;
3372 : }
3373 2303314 : 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 1324655 : EmitVariables(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption,
3421 : LetNotes *letNotes = NULL)
3422 : {
3423 1324655 : JS_ASSERT(pn->isArity(PN_LIST));
3424 1324655 : JS_ASSERT(!!letNotes == (emitOption == PushInitialValues));
3425 :
3426 1324655 : ptrdiff_t off = -1, noteIndex = -1;
3427 : ParseNode *next;
3428 1645006 : for (ParseNode *pn2 = pn->pn_head; ; pn2 = next) {
3429 1645006 : bool first = pn2 == pn->pn_head;
3430 1645006 : next = pn2->pn_next;
3431 :
3432 : ParseNode *pn3;
3433 1645006 : if (!pn2->isKind(PNK_NAME)) {
3434 : #if JS_HAS_DESTRUCTURING
3435 23909 : 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 20941 : JS_ASSERT(pn2->isKind(PNK_ASSIGN));
3459 20941 : JS_ASSERT(pn2->isOp(JSOP_NOP));
3460 20941 : 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 20941 : if (pn2->pn_left->isKind(PNK_NAME))
3471 : #endif
3472 : {
3473 7451 : pn3 = pn2->pn_right;
3474 7451 : pn2 = pn2->pn_left;
3475 7451 : goto do_name;
3476 : }
3477 :
3478 : #if JS_HAS_DESTRUCTURING
3479 13490 : ptrdiff_t stackDepthBefore = bce->stackDepth;
3480 13490 : JSOp op = JSOP_POP;
3481 13490 : 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 9818 : JS_ASSERT(noteIndex < 0 && !pn2->pn_next);
3489 9818 : if (letNotes) {
3490 1026 : if (!MaybeEmitLetGroupDecl(cx, bce, pn2, letNotes, &op))
3491 0 : return JS_FALSE;
3492 : } else {
3493 8792 : if (!MaybeEmitGroupAssignment(cx, bce, pn->getOp(), pn2, &op))
3494 0 : return JS_FALSE;
3495 : }
3496 : }
3497 13490 : if (op == JSOP_NOP) {
3498 533 : pn->pn_xflags = (pn->pn_xflags & ~PNX_POPVAR) | PNX_GROUPINIT;
3499 : } else {
3500 12957 : pn3 = pn2->pn_left;
3501 12957 : if (!EmitDestructuringDecls(cx, bce, pn->getOp(), pn3))
3502 0 : return JS_FALSE;
3503 :
3504 12957 : 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 9537 : ? OpToDeclType(pn->getOp())
3510 22494 : : SRC_DECL_NONE;
3511 :
3512 12957 : if (!EmitDestructuringOps(cx, bce, declType, pn3, letNotes))
3513 0 : return JS_FALSE;
3514 : }
3515 13490 : ptrdiff_t stackDepthAfter = bce->stackDepth;
3516 :
3517 : /* Give let ([] = x) a slot (see CheckDestructuring). */
3518 13490 : JS_ASSERT(stackDepthBefore <= stackDepthAfter);
3519 13490 : 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 13490 : if (emitOption != InitializeVars) {
3526 4338 : if (next)
3527 3159 : continue;
3528 1179 : break;
3529 : }
3530 9152 : 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 1621097 : pn3 = pn2->maybeExpr();
3541 :
3542 : do_name:
3543 1628548 : if (!BindNameToSlot(cx, bce, pn2))
3544 0 : return JS_FALSE;
3545 :
3546 : JSOp op;
3547 : jsatomid atomIndex;
3548 :
3549 1628548 : op = pn2->getOp();
3550 1628548 : 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 1628548 : JS_ASSERT(op != JSOP_CALLEE);
3557 1628548 : JS_ASSERT(!pn2->pn_cookie.isFree() || !pn->isOp(JSOP_NOP));
3558 1628548 : if (!MaybeEmitVarDecl(cx, bce, pn->getOp(), pn2, &atomIndex))
3559 0 : return JS_FALSE;
3560 :
3561 1628548 : if (pn3) {
3562 1243230 : JS_ASSERT(emitOption != DefineVars);
3563 1243230 : JS_ASSERT_IF(emitOption == PushInitialValues, op == JSOP_SETLOCAL);
3564 1243230 : if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
3565 155796 : JSOp bindOp = (op == JSOP_SETNAME) ? JSOP_BINDNAME : JSOP_BINDGNAME;
3566 155796 : if (!EmitIndex32(cx, bindOp, atomIndex, bce))
3567 0 : return false;
3568 : }
3569 1411676 : if (pn->isOp(JSOP_DEFCONST) &&
3570 168446 : !DefineCompileTimeConstant(cx, bce, pn2->pn_atom, pn3))
3571 : {
3572 0 : return JS_FALSE;
3573 : }
3574 :
3575 1243230 : unsigned oldflags = bce->flags;
3576 1243230 : bce->flags &= ~TCF_IN_FOR_INIT;
3577 1243230 : if (!EmitTree(cx, bce, pn3))
3578 0 : return JS_FALSE;
3579 1243230 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
3580 385318 : } 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 1628548 : if (emitOption != InitializeVars) {
3589 240620 : if (next)
3590 147788 : continue;
3591 92832 : break;
3592 : }
3593 :
3594 1387928 : JS_ASSERT_IF(pn2->isDefn(), pn3 == pn2->pn_expr);
3595 2606677 : if (first && NewSrcNote2(cx, bce, SRC_DECL,
3596 1218749 : (pn->isOp(JSOP_DEFCONST))
3597 : ? SRC_DECL_CONST
3598 1050308 : : (pn->isOp(JSOP_DEFVAR))
3599 : ? SRC_DECL_VAR
3600 2269057 : : SRC_DECL_LET) < 0) {
3601 0 : return JS_FALSE;
3602 : }
3603 1387928 : if (op == JSOP_ARGUMENTS) {
3604 0 : if (!EmitArguments(cx, bce))
3605 0 : return JS_FALSE;
3606 1387928 : } else if (!pn2->pn_cookie.isFree()) {
3607 1076548 : EMIT_UINT16_IMM_OP(op, atomIndex);
3608 : } else {
3609 311380 : if (!EmitIndexOp(cx, op, atomIndex, bce))
3610 0 : return false;
3611 : }
3612 :
3613 : #if JS_HAS_DESTRUCTURING
3614 : emit_note_pop:
3615 : #endif
3616 1397080 : ptrdiff_t tmp = bce->offset();
3617 1397080 : if (noteIndex >= 0) {
3618 169404 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp-off))
3619 0 : return JS_FALSE;
3620 : }
3621 1397080 : if (!next)
3622 1227676 : break;
3623 169404 : off = tmp;
3624 169404 : noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
3625 169404 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_POP) < 0)
3626 0 : return JS_FALSE;
3627 : }
3628 :
3629 1324655 : if (pn->pn_xflags & PNX_POPVAR) {
3630 1194203 : if (Emit1(cx, bce, JSOP_POP) < 0)
3631 0 : return JS_FALSE;
3632 : }
3633 :
3634 1324655 : return JS_TRUE;
3635 : }
3636 :
3637 : static bool
3638 3066977 : EmitAssignment(JSContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp op, ParseNode *rhs)
3639 : {
3640 3066977 : 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 3066977 : jsatomid atomIndex = (jsatomid) -1; /* quell GCC overwarning */
3648 3066977 : jsbytecode offset = 1;
3649 :
3650 3066977 : switch (lhs->getKind()) {
3651 : case PNK_NAME:
3652 708348 : if (!BindNameToSlot(cx, bce, lhs))
3653 0 : return false;
3654 708348 : if (!lhs->pn_cookie.isFree()) {
3655 570876 : JS_ASSERT(lhs->pn_cookie.level() == 0);
3656 570876 : atomIndex = lhs->pn_cookie.slot();
3657 : } else {
3658 137472 : if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
3659 0 : return false;
3660 137472 : if (!lhs->isConst()) {
3661 137472 : JSOp op = lhs->isOp(JSOP_SETGNAME) ? JSOP_BINDGNAME : JSOP_BINDNAME;
3662 137472 : if (!EmitIndex32(cx, op, atomIndex, bce))
3663 0 : return false;
3664 137472 : offset++;
3665 : }
3666 : }
3667 708348 : break;
3668 : case PNK_DOT:
3669 647204 : if (!EmitTree(cx, bce, lhs->expr()))
3670 0 : return false;
3671 647204 : offset++;
3672 647204 : if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
3673 0 : return false;
3674 647204 : break;
3675 : case PNK_LB:
3676 1706036 : JS_ASSERT(lhs->isArity(PN_BINARY));
3677 1706036 : if (!EmitTree(cx, bce, lhs->pn_left))
3678 0 : return false;
3679 1706036 : if (!EmitTree(cx, bce, lhs->pn_right))
3680 0 : return false;
3681 1706036 : offset += 2;
3682 1706036 : 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 3066977 : if (op != JSOP_NOP) {
3709 81263 : JS_ASSERT(rhs);
3710 81263 : switch (lhs->getKind()) {
3711 : case PNK_NAME:
3712 73757 : 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 73757 : } else if (lhs->isOp(JSOP_SETNAME)) {
3724 10057 : if (Emit1(cx, bce, JSOP_DUP) < 0)
3725 0 : return false;
3726 10057 : if (!EmitIndex32(cx, JSOP_GETXPROP, atomIndex, bce))
3727 0 : return false;
3728 63700 : } else if (lhs->isOp(JSOP_SETGNAME)) {
3729 21744 : if (!BindGlobal(cx, bce, lhs, lhs->pn_atom))
3730 0 : return false;
3731 21744 : if (!EmitAtomOp(cx, lhs, JSOP_GETGNAME, bce))
3732 0 : return false;
3733 : } else {
3734 41956 : EMIT_UINT16_IMM_OP(lhs->isOp(JSOP_SETARG) ? JSOP_GETARG : JSOP_GETLOCAL, atomIndex);
3735 : }
3736 73757 : break;
3737 : case PNK_DOT:
3738 6628 : if (Emit1(cx, bce, JSOP_DUP) < 0)
3739 0 : return false;
3740 6628 : 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 6628 : bool isLength = (lhs->pn_atom == cx->runtime->atomState.lengthAtom);
3747 6628 : if (!EmitIndex32(cx, isLength ? JSOP_LENGTH : JSOP_GETPROP, atomIndex, bce))
3748 0 : return false;
3749 : }
3750 6628 : break;
3751 : case PNK_LB:
3752 : case PNK_LP:
3753 : #if JS_HAS_XML_SUPPORT
3754 : case PNK_XMLUNARY:
3755 : #endif
3756 878 : if (Emit1(cx, bce, JSOP_DUP2) < 0)
3757 0 : return false;
3758 878 : if (!EmitElemOpBase(cx, bce, JSOP_GETELEM))
3759 0 : return false;
3760 878 : break;
3761 : default:;
3762 : }
3763 : }
3764 :
3765 : /* Now emit the right operand (it may affect the namespace). */
3766 3066977 : if (rhs) {
3767 3012393 : 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 54584 : 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 3066977 : 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 81263 : if (!lhs->isKind(PNK_NAME) || !lhs->isConst()) {
3783 81263 : if (NewSrcNote(cx, bce, SRC_ASSIGNOP) < 0)
3784 0 : return false;
3785 : }
3786 81263 : 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 10132358 : if (!lhs->isKind(PNK_NAME) &&
3792 : #if JS_HAS_DESTRUCTURING
3793 2358629 : !lhs->isKind(PNK_RB) &&
3794 2353476 : !lhs->isKind(PNK_RC) &&
3795 : #endif
3796 2353276 : NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - top) < 0)
3797 : {
3798 0 : return false;
3799 : }
3800 :
3801 : /* Finally, emit the specialized assignment bytecode. */
3802 3066977 : switch (lhs->getKind()) {
3803 : case PNK_NAME:
3804 708348 : 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 708348 : if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL)) {
3813 570876 : JS_ASSERT(atomIndex < UINT16_MAX);
3814 570876 : EMIT_UINT16_IMM_OP(lhs->getOp(), atomIndex);
3815 : } else {
3816 137472 : if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
3817 0 : return false;
3818 : }
3819 708348 : break;
3820 : case PNK_DOT:
3821 647204 : if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
3822 0 : return false;
3823 647204 : break;
3824 : case PNK_LB:
3825 : case PNK_LP:
3826 1706054 : if (Emit1(cx, bce, JSOP_SETELEM) < 0)
3827 0 : return false;
3828 1706054 : 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 3066977 : return true;
3847 : }
3848 :
3849 : #ifdef DEBUG
3850 : static JSBool
3851 28526 : GettableNoteForNextOp(BytecodeEmitter *bce)
3852 : {
3853 : ptrdiff_t offset, target;
3854 : jssrcnote *sn, *end;
3855 :
3856 28526 : offset = 0;
3857 28526 : target = bce->offset();
3858 4097094 : for (sn = bce->notes(), end = sn + bce->noteCount(); sn < end;
3859 4068568 : sn = SN_NEXT(sn)) {
3860 4068568 : if (offset == target && SN_IS_GETTABLE(sn))
3861 0 : return JS_TRUE;
3862 4068568 : offset += SN_DELTA(sn);
3863 : }
3864 28526 : return JS_FALSE;
3865 : }
3866 : #endif
3867 :
3868 : /* Top-level named functions need a nop for decompilation. */
3869 : static JSBool
3870 225617 : EmitFunctionDefNop(JSContext *cx, BytecodeEmitter *bce, unsigned index)
3871 : {
3872 225617 : return NewSrcNote2(cx, bce, SRC_FUNCDEF, (ptrdiff_t)index) >= 0 &&
3873 225617 : Emit1(cx, bce, JSOP_NOP) >= 0;
3874 : }
3875 :
3876 : static bool
3877 226874 : EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
3878 : {
3879 226874 : const size_t len = 1 + UINT32_INDEX_LEN;
3880 226874 : ptrdiff_t offset = EmitCheck(cx, bce, len);
3881 226874 : if (offset < 0)
3882 0 : return false;
3883 :
3884 226874 : jsbytecode *next = bce->next();
3885 226874 : next[0] = JSOP_NEWINIT;
3886 226874 : next[1] = jsbytecode(key);
3887 226874 : next[2] = 0;
3888 226874 : next[3] = 0;
3889 226874 : next[4] = 0;
3890 226874 : bce->current->next = next + len;
3891 226874 : UpdateDepth(cx, bce, offset);
3892 226874 : CheckTypeSet(cx, bce, JSOP_NEWINIT);
3893 226874 : return true;
3894 : }
3895 :
3896 : bool
3897 29699 : ParseNode::getConstantValue(JSContext *cx, bool strictChecks, Value *vp)
3898 : {
3899 29699 : switch (getKind()) {
3900 : case PNK_NUMBER:
3901 13988 : vp->setNumber(pn_dval);
3902 13988 : return true;
3903 : case PNK_STRING:
3904 6621 : vp->setString(pn_atom);
3905 6621 : 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 5745 : JS_ASSERT(isOp(JSOP_NEWINIT) && !(pn_xflags & PNX_NONCONST));
3917 :
3918 5745 : JSObject *obj = NewDenseAllocatedArray(cx, pn_count);
3919 5745 : if (!obj)
3920 0 : return false;
3921 :
3922 5745 : unsigned idx = 0;
3923 22856 : for (ParseNode *pn = pn_head; pn; idx++, pn = pn->pn_next) {
3924 : Value value;
3925 17111 : if (!pn->getConstantValue(cx, strictChecks, &value))
3926 0 : return false;
3927 17111 : if (!obj->defineGeneric(cx, INT_TO_JSID(idx), value, NULL, NULL, JSPROP_ENUMERATE))
3928 0 : return false;
3929 : }
3930 5745 : JS_ASSERT(idx == pn_count);
3931 :
3932 5745 : types::FixArrayType(cx, obj);
3933 5745 : vp->setObject(*obj);
3934 5745 : 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 7254 : EmitSingletonInitialiser(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
3982 : {
3983 : Value value;
3984 7254 : if (!pn->getConstantValue(cx, bce->needStrictChecks(), &value))
3985 0 : return false;
3986 :
3987 7254 : JS_ASSERT(value.isObject());
3988 7254 : ObjectBox *objbox = bce->parser->newObjectBox(&value.toObject());
3989 7254 : if (!objbox)
3990 0 : return false;
3991 :
3992 7254 : 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 59153270 : EmitLevelManager(BytecodeEmitter *bce) : bce(bce) { bce->emitLevel++; }
4004 59153270 : ~EmitLevelManager() { bce->emitLevel--; }
4005 : };
4006 :
4007 : static bool
4008 224906 : 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 224906 : StmtInfo *stmt = bce->topStmt;
4017 224906 : JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE));
4018 224906 : stmt->type = STMT_CATCH;
4019 224906 : catchStart = stmt->update;
4020 :
4021 : /* Go up one statement info record to the TRY or FINALLY record. */
4022 224906 : stmt = stmt->down;
4023 224906 : 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 224906 : 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 224906 : if (pn->pn_kid2 && Emit1(cx, bce, JSOP_DUP) < 0)
4034 0 : return false;
4035 :
4036 224906 : ParseNode *pn2 = pn->pn_kid1;
4037 224906 : 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 224906 : JS_ASSERT(!pn2->pn_cookie.isFree());
4051 224906 : EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_cookie.asInteger());
4052 224906 : break;
4053 :
4054 : default:
4055 0 : JS_ASSERT(0);
4056 : }
4057 :
4058 : /* Emit the guard expression, if there is one. */
4059 224906 : if (pn->pn_kid2) {
4060 1214 : if (!EmitTree(cx, bce, pn->pn_kid2))
4061 0 : return false;
4062 1214 : if (!SetSrcNoteOffset(cx, bce, CATCHNOTE(*stmt), 0, bce->offset() - catchStart))
4063 0 : return false;
4064 : /* ifeq <next block> */
4065 1214 : guardJump = EmitJump(cx, bce, JSOP_IFEQ, 0);
4066 1214 : if (guardJump < 0)
4067 0 : return false;
4068 1214 : GUARDJUMP(*stmt) = guardJump;
4069 :
4070 : /* Pop duplicated exception object as we no longer need it. */
4071 1214 : if (Emit1(cx, bce, JSOP_POP) < 0)
4072 0 : return false;
4073 : }
4074 :
4075 : /* Emit the catch body. */
4076 224906 : 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 224906 : ptrdiff_t off = bce->stackDepth;
4084 224906 : if (NewSrcNote2(cx, bce, SRC_CATCH, off) < 0)
4085 0 : return false;
4086 224906 : 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 240738 : EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4095 : {
4096 : StmtInfo stmtInfo;
4097 240738 : 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 240738 : 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 240738 : int depth = bce->stackDepth;
4120 :
4121 : /* Mark try location for decompilation, then emit try block. */
4122 240738 : if (Emit1(cx, bce, JSOP_TRY) < 0)
4123 0 : return false;
4124 240738 : ptrdiff_t tryStart = bce->offset();
4125 240738 : if (!EmitTree(cx, bce, pn->pn_kid1))
4126 0 : return false;
4127 240738 : JS_ASSERT(depth == bce->stackDepth);
4128 :
4129 : /* GOSUB to finally, if present. */
4130 240738 : if (pn->pn_kid3) {
4131 21991 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4132 0 : return false;
4133 21991 : 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 240738 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4139 0 : return false;
4140 240738 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &catchJump) < 0)
4141 0 : return false;
4142 :
4143 240738 : ptrdiff_t tryEnd = bce->offset();
4144 :
4145 : /* If this try has a catch block, emit it. */
4146 240738 : ParseNode *lastCatch = NULL;
4147 240738 : if (ParseNode *pn2 = pn->pn_kid2) {
4148 224675 : 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 449581 : for (ParseNode *pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
4173 : ptrdiff_t guardJump, catchNote;
4174 :
4175 224906 : JS_ASSERT(bce->stackDepth == depth);
4176 224906 : guardJump = GUARDJUMP(stmtInfo);
4177 224906 : 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 224906 : catchNote = NewSrcNote2(cx, bce, SRC_CATCH, 0);
4212 224906 : if (catchNote < 0)
4213 0 : return false;
4214 224906 : 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 224906 : JS_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
4222 224906 : count = pn3->pn_objbox->object->asStaticBlock().slotCount();
4223 224906 : if (!EmitTree(cx, bce, pn3))
4224 0 : return false;
4225 :
4226 : /* gosub <finally>, if required */
4227 224906 : if (pn->pn_kid3) {
4228 5928 : if (EmitBackPatchOp(cx, bce, JSOP_BACKPATCH, &GOSUBS(stmtInfo)) < 0)
4229 0 : return false;
4230 5928 : 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 224906 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
4238 0 : return false;
4239 224906 : 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 224906 : 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 240738 : if (lastCatch && lastCatch->pn_kid2) {
4257 983 : SetJumpOffsetAt(bce, GUARDJUMP(stmtInfo));
4258 :
4259 : /* Sync the stack to take into account pushed exception. */
4260 983 : JS_ASSERT(bce->stackDepth == depth);
4261 983 : bce->stackDepth = depth + 1;
4262 :
4263 : /*
4264 : * Rethrow the exception, delegating executing of finally if any
4265 : * to the exception handler.
4266 : */
4267 983 : if (NewSrcNote(cx, bce, SRC_HIDDEN) < 0 || Emit1(cx, bce, JSOP_THROW) < 0)
4268 0 : return false;
4269 : }
4270 :
4271 240738 : JS_ASSERT(bce->stackDepth == depth);
4272 :
4273 : /* Emit finally handler if any. */
4274 240738 : ptrdiff_t finallyStart = 0; /* to quell GCC uninitialized warnings */
4275 240738 : 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 21991 : if (!BackPatch(cx, bce, GOSUBS(stmtInfo), bce->next(), JSOP_GOSUB))
4281 0 : return false;
4282 :
4283 21991 : finallyStart = bce->offset();
4284 :
4285 : /* Indicate that we're emitting a subroutine body. */
4286 21991 : stmtInfo.type = STMT_SUBROUTINE;
4287 21991 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_kid3->pn_pos.begin.lineno))
4288 0 : return false;
4289 65973 : if (Emit1(cx, bce, JSOP_FINALLY) < 0 ||
4290 21991 : !EmitTree(cx, bce, pn->pn_kid3) ||
4291 21991 : Emit1(cx, bce, JSOP_RETSUB) < 0)
4292 : {
4293 0 : return false;
4294 : }
4295 21991 : JS_ASSERT(bce->stackDepth == depth);
4296 : }
4297 240738 : if (!PopStatementBCE(cx, bce))
4298 0 : return false;
4299 :
4300 240738 : 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 240738 : 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 240738 : 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 240738 : if (pn->pn_kid3 && !NewTryNote(cx, bce, JSTRY_FINALLY, depth, tryStart, finallyStart))
4320 0 : return false;
4321 :
4322 240738 : return true;
4323 : }
4324 :
4325 : static bool
4326 927490 : 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 927490 : stmtInfo.type = STMT_IF;
4332 927490 : ptrdiff_t beq = -1;
4333 927490 : ptrdiff_t jmp = -1;
4334 927490 : ptrdiff_t noteIndex = -1;
4335 :
4336 : if_again:
4337 : /* Emit code for the condition before pushing stmtInfo. */
4338 947267 : if (!EmitTree(cx, bce, pn->pn_kid1))
4339 0 : return JS_FALSE;
4340 947267 : ptrdiff_t top = bce->offset();
4341 947267 : if (stmtInfo.type == STMT_IF) {
4342 927490 : 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 947267 : ParseNode *pn3 = pn->pn_kid3;
4364 947267 : noteIndex = NewSrcNote(cx, bce, pn3 ? SRC_IF_ELSE : SRC_IF);
4365 947267 : if (noteIndex < 0)
4366 0 : return JS_FALSE;
4367 947267 : beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
4368 947267 : if (beq < 0)
4369 0 : return JS_FALSE;
4370 :
4371 : /* Emit code for the then and optional else parts. */
4372 947267 : if (!EmitTree(cx, bce, pn->pn_kid2))
4373 0 : return JS_FALSE;
4374 947267 : if (pn3) {
4375 : /* Modify stmtInfo so we know we're in the else part. */
4376 156231 : 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 156231 : jmp = EmitGoto(cx, bce, &stmtInfo, &stmtInfo.breaks);
4385 156231 : if (jmp < 0)
4386 0 : return JS_FALSE;
4387 :
4388 : /* Ensure the branch-if-false comes here, then emit the else. */
4389 156231 : SetJumpOffsetAt(bce, beq);
4390 156231 : if (pn3->isKind(PNK_IF)) {
4391 19777 : pn = pn3;
4392 19777 : goto if_again;
4393 : }
4394 :
4395 136454 : 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 136454 : 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 791036 : SetJumpOffsetAt(bce, beq);
4410 : }
4411 927490 : 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 45060 : EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
4459 : {
4460 45060 : JS_ASSERT(pnLet->isArity(PN_BINARY));
4461 45060 : ParseNode *varList = pnLet->pn_left;
4462 45060 : JS_ASSERT(varList->isArity(PN_LIST));
4463 45060 : ParseNode *letBody = pnLet->pn_right;
4464 45060 : JS_ASSERT(letBody->isLet() && letBody->isKind(PNK_LEXICALSCOPE));
4465 45060 : StaticBlockObject &blockObj = letBody->pn_objbox->object->asStaticBlock();
4466 :
4467 45060 : ptrdiff_t letHeadOffset = bce->offset();
4468 45060 : int letHeadDepth = bce->stackDepth;
4469 :
4470 90120 : LetNotes letNotes(cx);
4471 45060 : 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 45060 : uint32_t alreadyPushed = unsigned(bce->stackDepth - letHeadDepth);
4476 45060 : uint32_t blockObjCount = blockObj.slotCount();
4477 56702 : for (uint32_t i = alreadyPushed; i < blockObjCount; ++i) {
4478 : /* Tell the decompiler not to print the decl in the let head. */
4479 11642 : if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
4480 0 : return false;
4481 11642 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
4482 0 : return false;
4483 : }
4484 :
4485 : StmtInfo stmtInfo;
4486 45060 : PushBlockScope(bce, &stmtInfo, blockObj, bce->offset());
4487 :
4488 45060 : if (!letNotes.update(cx, bce, bce->offset()))
4489 0 : return false;
4490 :
4491 45060 : ptrdiff_t declNote = NewSrcNote(cx, bce, SRC_DECL);
4492 45060 : if (declNote < 0)
4493 0 : return false;
4494 :
4495 45060 : ptrdiff_t bodyBegin = bce->offset();
4496 45060 : if (!EmitEnterBlock(cx, bce, letBody, JSOP_ENTERLET0))
4497 0 : return false;
4498 :
4499 45060 : if (!EmitTree(cx, bce, letBody->pn_expr))
4500 0 : return false;
4501 :
4502 45060 : JSOp leaveOp = letBody->getOp();
4503 45060 : if (leaveOp == JSOP_LEAVEBLOCKEXPR) {
4504 1538 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - letHeadOffset) < 0)
4505 0 : return false;
4506 : }
4507 :
4508 45060 : JS_ASSERT(leaveOp == JSOP_LEAVEBLOCK || leaveOp == JSOP_LEAVEBLOCKEXPR);
4509 45060 : EMIT_UINT16_IMM_OP(leaveOp, blockObj.slotCount());
4510 :
4511 45060 : ptrdiff_t bodyEnd = bce->offset();
4512 45060 : JS_ASSERT(bodyEnd > bodyBegin);
4513 :
4514 45060 : if (!PopStatementBCE(cx, bce))
4515 0 : return false;
4516 :
4517 : ptrdiff_t o = PackLetData((bodyEnd - bodyBegin) -
4518 : (JSOP_ENTERLET0_LENGTH + JSOP_LEAVEBLOCK_LENGTH),
4519 45060 : letNotes.isGroupAssign());
4520 45060 : 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 366155 : EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
4607 : {
4608 366155 : JS_ASSERT(pn->isKind(PNK_LEXICALSCOPE));
4609 366155 : JS_ASSERT(pn->getOp() == JSOP_LEAVEBLOCK);
4610 :
4611 : StmtInfo stmtInfo;
4612 366155 : ObjectBox *objbox = pn->pn_objbox;
4613 366155 : StaticBlockObject &blockObj = objbox->object->asStaticBlock();
4614 366155 : 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 366155 : ptrdiff_t noteIndex = -1;
4624 752050 : if (pn->expr()->getKind() != PNK_FOR &&
4625 353308 : pn->expr()->getKind() != PNK_CATCH &&
4626 : (stmtInfo.down
4627 : ? stmtInfo.down->type == STMT_BLOCK &&
4628 32515 : (!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 85767 : JS_ASSERT(bce->noteCount() == 0 ||
4633 : bce->lastNoteOffset() != bce->offset() ||
4634 85767 : !GettableNoteForNextOp(bce));
4635 28661 : noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0);
4636 28661 : if (noteIndex < 0)
4637 0 : return false;
4638 : }
4639 :
4640 366155 : ptrdiff_t bodyBegin = bce->offset();
4641 366155 : if (!EmitEnterBlock(cx, bce, pn, JSOP_ENTERBLOCK))
4642 0 : return false;
4643 :
4644 366155 : if (!EmitTree(cx, bce, pn->pn_expr))
4645 0 : return false;
4646 :
4647 366155 : if (noteIndex >= 0) {
4648 28661 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, bce->offset() - bodyBegin))
4649 0 : return false;
4650 : }
4651 :
4652 366155 : EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, blockObj.slotCount());
4653 :
4654 366155 : 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 7096 : SetMethodFunction(JSContext *cx, FunctionBox *funbox, JSAtom *atom)
4676 : {
4677 14192 : RootedVarObject parent(cx);
4678 7096 : parent = funbox->function()->getParent();
4679 :
4680 : /*
4681 : * Replace a boxed function with a new one with a method atom. Methods
4682 : * require a function with the extended size finalize kind, which normal
4683 : * functions don't have. We don't eagerly allocate functions with the
4684 : * expanded size for boxed functions, as most functions are not methods.
4685 : */
4686 : JSFunction *fun = js_NewFunction(cx, NULL, NULL,
4687 7096 : funbox->function()->nargs,
4688 7096 : funbox->function()->flags,
4689 : parent,
4690 7096 : funbox->function()->atom,
4691 21288 : JSFunction::ExtendedFinalizeKind);
4692 7096 : if (!fun)
4693 0 : return false;
4694 :
4695 7096 : JSScript *script = funbox->function()->script();
4696 7096 : if (script) {
4697 2542 : fun->setScript(script);
4698 2542 : if (!script->typeSetFunction(cx, fun))
4699 0 : return false;
4700 : }
4701 :
4702 7096 : JS_ASSERT(funbox->function()->joinable());
4703 7096 : fun->setJoinable();
4704 :
4705 7096 : fun->setMethodAtom(atom);
4706 :
4707 7096 : funbox->object = fun;
4708 7096 : return true;
4709 : }
4710 :
4711 : static bool
4712 54584 : EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
4713 : {
4714 : StmtInfo stmtInfo;
4715 54584 : PushStatement(bce, &stmtInfo, STMT_FOR_IN_LOOP, top);
4716 :
4717 54584 : ParseNode *forHead = pn->pn_left;
4718 54584 : ParseNode *forBody = pn->pn_right;
4719 :
4720 54584 : ParseNode *pn1 = forHead->pn_kid1;
4721 54584 : bool letDecl = pn1 && pn1->isKind(PNK_LEXICALSCOPE);
4722 54584 : JS_ASSERT_IF(letDecl, pn1->isLet());
4723 :
4724 54584 : StaticBlockObject *blockObj = letDecl ? &pn1->pn_objbox->object->asStaticBlock() : NULL;
4725 54584 : uint32_t blockObjCount = blockObj ? blockObj->slotCount() : 0;
4726 :
4727 54584 : if (letDecl) {
4728 : /*
4729 : * The let's slot(s) will be under the iterator, but the block must not
4730 : * be entered (i.e. fp->blockChain set) until after evaluating the rhs.
4731 : * Thus, push to reserve space and enterblock after. The same argument
4732 : * applies when leaving the loop. Thus, a for-let-in loop looks like:
4733 : *
4734 : * push x N
4735 : * eval rhs
4736 : * iter
4737 : * enterlet1
4738 : * goto
4739 : * ... loop body
4740 : * ifne
4741 : * leaveforinlet
4742 : * enditer
4743 : * popn(N)
4744 : */
4745 65030 : for (uint32_t i = 0; i < blockObjCount; ++i) {
4746 33663 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
4747 0 : return false;
4748 : }
4749 : }
4750 :
4751 : /*
4752 : * If the left part is 'var x', emit code to define x if necessary
4753 : * using a prolog opcode, but do not emit a pop. If the left part was
4754 : * originally 'var x = i', the parser will have rewritten it; see
4755 : * Parser::forStatement. 'for (let x = i in o)' is mercifully banned.
4756 : */
4757 54584 : if (pn1) {
4758 51919 : ParseNode *decl = letDecl ? pn1->pn_expr : pn1;
4759 51919 : JS_ASSERT(decl->isKind(PNK_VAR) || decl->isKind(PNK_LET));
4760 51919 : bce->flags |= TCF_IN_FOR_INIT;
4761 51919 : if (!EmitVariables(cx, bce, decl, DefineVars))
4762 0 : return false;
4763 51919 : bce->flags &= ~TCF_IN_FOR_INIT;
4764 : }
4765 :
4766 : /* Compile the object expression to the right of 'in'. */
4767 54584 : if (!EmitTree(cx, bce, forHead->pn_kid3))
4768 0 : return JS_FALSE;
4769 :
4770 : /*
4771 : * Emit a bytecode to convert top of stack value to the iterator
4772 : * object depending on the loop variant (for-in, for-each-in, or
4773 : * destructuring for-in).
4774 : */
4775 54584 : JS_ASSERT(pn->isOp(JSOP_ITER));
4776 54584 : if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
4777 0 : return false;
4778 :
4779 : /* Enter the block before the loop body, after evaluating the obj. */
4780 : StmtInfo letStmt;
4781 54584 : if (letDecl) {
4782 31367 : PushBlockScope(bce, &letStmt, *blockObj, bce->offset());
4783 31367 : letStmt.flags |= SIF_FOR_BLOCK;
4784 31367 : if (!EmitEnterBlock(cx, bce, pn1, JSOP_ENTERLET1))
4785 0 : return false;
4786 : }
4787 :
4788 : /* Annotate so the decompiler can find the loop-closing jump. */
4789 54584 : int noteIndex = NewSrcNote(cx, bce, SRC_FOR_IN);
4790 54584 : if (noteIndex < 0)
4791 0 : return false;
4792 :
4793 : /*
4794 : * Jump down to the loop condition to minimize overhead assuming at
4795 : * least one iteration, as the other loop forms do.
4796 : */
4797 54584 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
4798 54584 : if (jmp < 0)
4799 0 : return false;
4800 :
4801 54584 : top = bce->offset();
4802 54584 : SET_STATEMENT_TOP(&stmtInfo, top);
4803 54584 : if (EmitLoopHead(cx, bce, NULL) < 0)
4804 0 : return false;
4805 :
4806 : #ifdef DEBUG
4807 54584 : int loopDepth = bce->stackDepth;
4808 : #endif
4809 :
4810 : /*
4811 : * Emit code to get the next enumeration value and assign it to the
4812 : * left hand side. The JSOP_POP after this assignment is annotated
4813 : * so that the decompiler can distinguish 'for (x in y)' from
4814 : * 'for (var x in y)'.
4815 : */
4816 54584 : if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, NULL))
4817 0 : return false;
4818 :
4819 54584 : ptrdiff_t tmp2 = bce->offset();
4820 106503 : if (forHead->pn_kid1 && NewSrcNote2(cx, bce, SRC_DECL,
4821 51919 : (forHead->pn_kid1->isOp(JSOP_DEFVAR))
4822 : ? SRC_DECL_VAR
4823 51919 : : SRC_DECL_LET) < 0) {
4824 0 : return false;
4825 : }
4826 54584 : if (Emit1(cx, bce, JSOP_POP) < 0)
4827 0 : return false;
4828 :
4829 : /* The stack should be balanced around the assignment opcode sequence. */
4830 54584 : JS_ASSERT(bce->stackDepth == loopDepth);
4831 :
4832 : /* Emit code for the loop body. */
4833 54584 : if (!EmitTree(cx, bce, forBody))
4834 0 : return false;
4835 :
4836 : /* Set loop and enclosing "update" offsets, for continue. */
4837 54584 : StmtInfo *stmt = &stmtInfo;
4838 54694 : do {
4839 54694 : stmt->update = bce->offset();
4840 : } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
4841 :
4842 : /*
4843 : * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
4844 : */
4845 54584 : SetJumpOffsetAt(bce, jmp);
4846 54584 : if (!EmitLoopEntry(cx, bce, NULL))
4847 0 : return false;
4848 54584 : if (Emit1(cx, bce, JSOP_MOREITER) < 0)
4849 0 : return false;
4850 54584 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
4851 54584 : if (beq < 0)
4852 0 : return false;
4853 :
4854 : /* Set the first srcnote offset so we can find the start of the loop body. */
4855 54584 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp2 - jmp))
4856 0 : return false;
4857 : /* Set the second srcnote offset so we can find the closing jump. */
4858 54584 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, beq - jmp))
4859 0 : return false;
4860 :
4861 : /* Fixup breaks and continues before JSOP_ITER (and JSOP_LEAVEFORINLET). */
4862 54584 : if (!PopStatementBCE(cx, bce))
4863 0 : return false;
4864 :
4865 54584 : if (letDecl) {
4866 31367 : if (!PopStatementBCE(cx, bce))
4867 0 : return false;
4868 31367 : if (Emit1(cx, bce, JSOP_LEAVEFORLETIN) < 0)
4869 0 : return false;
4870 : }
4871 :
4872 54584 : if (!NewTryNote(cx, bce, JSTRY_ITER, bce->stackDepth, top, bce->offset()))
4873 0 : return false;
4874 54584 : if (Emit1(cx, bce, JSOP_ENDITER) < 0)
4875 0 : return false;
4876 :
4877 54584 : if (letDecl) {
4878 : /* Tell the decompiler to pop but not to print. */
4879 31367 : if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
4880 0 : return false;
4881 31367 : EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
4882 : }
4883 :
4884 54584 : return true;
4885 : }
4886 :
4887 : static bool
4888 67414 : EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
4889 : {
4890 : StmtInfo stmtInfo;
4891 67414 : PushStatement(bce, &stmtInfo, STMT_FOR_LOOP, top);
4892 :
4893 67414 : ParseNode *forHead = pn->pn_left;
4894 67414 : ParseNode *forBody = pn->pn_right;
4895 :
4896 : /* C-style for (init; cond; update) ... loop. */
4897 67414 : JSOp op = JSOP_POP;
4898 67414 : ParseNode *pn3 = forHead->pn_kid1;
4899 67414 : if (!pn3) {
4900 : /* No initializer: emit an annotated nop for the decompiler. */
4901 32566 : op = JSOP_NOP;
4902 : } else {
4903 34848 : bce->flags |= TCF_IN_FOR_INIT;
4904 : #if JS_HAS_DESTRUCTURING
4905 34848 : if (pn3->isKind(PNK_ASSIGN)) {
4906 3128 : JS_ASSERT(pn3->isOp(JSOP_NOP));
4907 3128 : if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, &op))
4908 0 : return false;
4909 : }
4910 : #endif
4911 34848 : if (op == JSOP_POP) {
4912 34848 : if (!EmitTree(cx, bce, pn3))
4913 0 : return false;
4914 34848 : if (pn3->isKind(PNK_VAR) || pn3->isKind(PNK_CONST) || pn3->isKind(PNK_LET)) {
4915 : /*
4916 : * Check whether a destructuring-initialized var decl
4917 : * was optimized to a group assignment. If so, we do
4918 : * not need to emit a pop below, so switch to a nop,
4919 : * just for the decompiler.
4920 : */
4921 31639 : JS_ASSERT(pn3->isArity(PN_LIST) || pn3->isArity(PN_BINARY));
4922 31639 : if (pn3->pn_xflags & PNX_GROUPINIT)
4923 0 : op = JSOP_NOP;
4924 : }
4925 : }
4926 34848 : bce->flags &= ~TCF_IN_FOR_INIT;
4927 : }
4928 :
4929 : /*
4930 : * NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
4931 : * Use tmp to hold the biased srcnote "top" offset, which differs
4932 : * from the top local variable by the length of the JSOP_GOTO
4933 : * emitted in between tmp and top if this loop has a condition.
4934 : */
4935 67414 : int noteIndex = NewSrcNote(cx, bce, SRC_FOR);
4936 67414 : if (noteIndex < 0 || Emit1(cx, bce, op) < 0)
4937 0 : return false;
4938 67414 : ptrdiff_t tmp = bce->offset();
4939 :
4940 67414 : ptrdiff_t jmp = -1;
4941 67414 : if (forHead->pn_kid2) {
4942 : /* Goto the loop condition, which branches back to iterate. */
4943 66188 : jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
4944 66188 : if (jmp < 0)
4945 0 : return false;
4946 : } else {
4947 1226 : if (op != JSOP_NOP && Emit1(cx, bce, JSOP_NOP) < 0)
4948 0 : return false;
4949 : }
4950 :
4951 67414 : top = bce->offset();
4952 67414 : SET_STATEMENT_TOP(&stmtInfo, top);
4953 :
4954 : /* Emit code for the loop body. */
4955 67414 : if (EmitLoopHead(cx, bce, forBody) < 0)
4956 0 : return false;
4957 67414 : if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody))
4958 0 : return false;
4959 67414 : if (!EmitTree(cx, bce, forBody))
4960 0 : return false;
4961 :
4962 : /* Set the second note offset so we can find the update part. */
4963 67414 : JS_ASSERT(noteIndex != -1);
4964 67414 : ptrdiff_t tmp2 = bce->offset();
4965 :
4966 : /* Set loop and enclosing "update" offsets, for continue. */
4967 67414 : StmtInfo *stmt = &stmtInfo;
4968 67434 : do {
4969 67434 : stmt->update = bce->offset();
4970 : } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
4971 :
4972 : /* Check for update code to do before the condition (if any). */
4973 67414 : pn3 = forHead->pn_kid3;
4974 67414 : if (pn3) {
4975 65385 : op = JSOP_POP;
4976 : #if JS_HAS_DESTRUCTURING
4977 65385 : if (pn3->isKind(PNK_ASSIGN)) {
4978 1151 : JS_ASSERT(pn3->isOp(JSOP_NOP));
4979 1151 : if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, &op))
4980 0 : return false;
4981 : }
4982 : #endif
4983 65385 : if (op == JSOP_POP && !EmitTree(cx, bce, pn3))
4984 0 : return false;
4985 :
4986 : /* Always emit the POP or NOP, to help the decompiler. */
4987 65385 : if (Emit1(cx, bce, op) < 0)
4988 0 : return false;
4989 :
4990 : /* Restore the absolute line number for source note readers. */
4991 65385 : ptrdiff_t lineno = pn->pn_pos.end.lineno;
4992 65385 : if (bce->currentLine() != (unsigned) lineno) {
4993 63085 : if (NewSrcNote2(cx, bce, SRC_SETLINE, lineno) < 0)
4994 0 : return false;
4995 63085 : bce->current->currentLine = (unsigned) lineno;
4996 : }
4997 : }
4998 :
4999 67414 : ptrdiff_t tmp3 = bce->offset();
5000 :
5001 67414 : if (forHead->pn_kid2) {
5002 : /* Fix up the goto from top to target the loop condition. */
5003 66188 : JS_ASSERT(jmp >= 0);
5004 66188 : SetJumpOffsetAt(bce, jmp);
5005 66188 : if (!EmitLoopEntry(cx, bce, forHead->pn_kid2))
5006 0 : return false;
5007 :
5008 66188 : if (!EmitTree(cx, bce, forHead->pn_kid2))
5009 0 : return false;
5010 : }
5011 :
5012 : /* Set the first note offset so we can find the loop condition. */
5013 67414 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp3 - tmp))
5014 0 : return false;
5015 67414 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 1, tmp2 - tmp))
5016 0 : return false;
5017 : /* The third note offset helps us find the loop-closing jump. */
5018 67414 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 2, bce->offset() - tmp))
5019 0 : return false;
5020 :
5021 : /* If no loop condition, just emit a loop-closing jump. */
5022 67414 : op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
5023 67414 : if (EmitJump(cx, bce, op, top - bce->offset()) < 0)
5024 0 : return false;
5025 :
5026 : /* Now fixup all breaks and continues. */
5027 67414 : return PopStatementBCE(cx, bce);
5028 : }
5029 :
5030 : static inline bool
5031 121998 : EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5032 : {
5033 121998 : JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
5034 121998 : return pn->pn_left->isKind(PNK_FORIN)
5035 : ? EmitForIn(cx, bce, pn, top)
5036 121998 : : EmitNormalFor(cx, bce, pn, top);
5037 : }
5038 :
5039 : static bool
5040 1017444 : EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5041 : {
5042 : #if JS_HAS_XML_SUPPORT
5043 1017444 : if (pn->isArity(PN_NULLARY))
5044 36 : return Emit1(cx, bce, JSOP_GETFUNNS) >= 0;
5045 : #endif
5046 :
5047 1017408 : JSFunction *fun = pn->pn_funbox->function();
5048 1017408 : JS_ASSERT(fun->isInterpreted());
5049 1017408 : if (fun->script()) {
5050 : /*
5051 : * This second pass is needed to emit JSOP_NOP with a source note
5052 : * for the already-emitted function definition prolog opcode. See
5053 : * comments in the PNK_STATEMENTLIST case.
5054 : */
5055 40925 : JS_ASSERT(pn->isOp(JSOP_NOP));
5056 40925 : JS_ASSERT(bce->inFunction());
5057 40925 : return EmitFunctionDefNop(cx, bce, pn->pn_index);
5058 : }
5059 :
5060 68170 : JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
5061 1044653 : fun->kind() == JSFUN_INTERPRETED);
5062 :
5063 : {
5064 : /*
5065 : * Generate code for the function's body. bce2 is not allocated on the
5066 : * stack because doing so significantly reduces the maximum depth of
5067 : * nested functions we can handle. See bug 696284.
5068 : */
5069 1952966 : AutoPtr<BytecodeEmitter> bce2(cx);
5070 976483 : bce2 = cx->new_<BytecodeEmitter>(bce->parser, pn->pn_pos.begin.lineno);
5071 976483 : if (!bce2 || !bce2->init(cx))
5072 0 : return false;
5073 :
5074 976483 : bce2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION |
5075 976483 : (bce->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
5076 976483 : bce2->bindings.transfer(cx, &pn->pn_funbox->bindings);
5077 976483 : bce2->setFunction(fun);
5078 976483 : bce2->funbox = pn->pn_funbox;
5079 976483 : bce2->parent = bce;
5080 976483 : bce2->globalScope = bce->globalScope;
5081 :
5082 : /*
5083 : * js::frontend::SetStaticLevel limited static nesting depth to fit in
5084 : * 16 bits and to reserve the all-ones value, thereby reserving the
5085 : * magic FREE_UPVAR_COOKIE value. Note the bce2->staticLevel assignment
5086 : * below.
5087 : */
5088 976483 : JS_ASSERT(bce->staticLevel < JS_BITMASK(16) - 1);
5089 976483 : bce2->staticLevel = bce->staticLevel + 1;
5090 :
5091 : /* We measured the max scope depth when we parsed the function. */
5092 976483 : if (!EmitFunctionScript(cx, bce2.get(), pn->pn_body))
5093 0 : return false;
5094 : }
5095 :
5096 : /* Make the function object a literal in the outer script's pool. */
5097 976483 : unsigned index = bce->objectList.index(pn->pn_funbox);
5098 :
5099 : /* Emit a bytecode pointing to the closure object in its immediate. */
5100 976483 : if (pn->getOp() != JSOP_NOP) {
5101 751043 : if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) &&
5102 177 : NewSrcNote(cx, bce, SRC_GENEXP) < 0)
5103 : {
5104 0 : return false;
5105 : }
5106 :
5107 750866 : return EmitFunctionOp(cx, pn->getOp(), index, bce);
5108 : }
5109 :
5110 : /*
5111 : * For a script we emit the code as we parse. Thus the bytecode for
5112 : * top-level functions should go in the prolog to predefine their
5113 : * names in the variable object before the already-generated main code
5114 : * is executed. This extra work for top-level scripts is not necessary
5115 : * when we emit the code for a function. It is fully parsed prior to
5116 : * invocation of the emitter and calls to EmitTree for function
5117 : * definitions can be scheduled before generating the rest of code.
5118 : */
5119 225617 : if (!bce->inFunction()) {
5120 184692 : JS_ASSERT(!bce->topStmt);
5121 184692 : if (!BindGlobal(cx, bce, pn, fun->atom))
5122 0 : return false;
5123 184692 : if (pn->pn_cookie.isFree()) {
5124 162676 : bce->switchToProlog();
5125 162676 : MOZ_ASSERT(!fun->isFlatClosure(),
5126 162676 : "global functions can't have upvars, so they are never flat");
5127 162676 : if (!EmitFunctionOp(cx, JSOP_DEFFUN, index, bce))
5128 0 : return false;
5129 162676 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
5130 0 : return false;
5131 162676 : bce->switchToMain();
5132 : }
5133 :
5134 : /* Emit NOP for the decompiler. */
5135 184692 : if (!EmitFunctionDefNop(cx, bce, index))
5136 0 : return false;
5137 : } else {
5138 : unsigned slot;
5139 81850 : DebugOnly<BindingKind> kind = bce->bindings.lookup(cx, fun->atom, &slot);
5140 40925 : JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
5141 40925 : JS_ASSERT(index < JS_BIT(20));
5142 40925 : pn->pn_index = index;
5143 40925 : JSOp op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
5144 81138 : if (pn->isClosed() &&
5145 20165 : !bce->callsEval() &&
5146 20048 : !bce->closedVars.append(pn->pn_cookie.slot()))
5147 : {
5148 0 : return false;
5149 : }
5150 40925 : return EmitSlotObjectOp(cx, op, slot, index, bce);
5151 : }
5152 :
5153 184692 : return true;
5154 : }
5155 :
5156 : static bool
5157 382 : EmitDo(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5158 : {
5159 : /* Emit an annotated nop so we know to decompile a 'do' keyword. */
5160 382 : ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
5161 382 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
5162 0 : return false;
5163 :
5164 382 : ptrdiff_t noteIndex2 = NewSrcNote(cx, bce, SRC_WHILE);
5165 382 : if (noteIndex2 < 0)
5166 0 : return false;
5167 :
5168 : /* Compile the loop body. */
5169 382 : ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left);
5170 382 : if (top < 0)
5171 0 : return false;
5172 382 : if (!EmitLoopEntry(cx, bce, NULL))
5173 0 : return false;
5174 :
5175 : StmtInfo stmtInfo;
5176 382 : PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
5177 382 : if (!EmitTree(cx, bce, pn->pn_left))
5178 0 : return false;
5179 :
5180 : /* Set loop and enclosing label update offsets, for continue. */
5181 382 : ptrdiff_t off = bce->offset();
5182 382 : StmtInfo *stmt = &stmtInfo;
5183 382 : do {
5184 382 : stmt->update = off;
5185 : } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
5186 :
5187 : /* Compile the loop condition, now that continues know where to go. */
5188 382 : if (!EmitTree(cx, bce, pn->pn_right))
5189 0 : return false;
5190 :
5191 : /*
5192 : * Since we use JSOP_IFNE for other purposes as well as for do-while
5193 : * loops, we must store 1 + (beq - top) in the SRC_WHILE note offset,
5194 : * and the decompiler must get that delta and decompile recursively.
5195 : */
5196 382 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
5197 382 : if (beq < 0)
5198 0 : return false;
5199 :
5200 : /*
5201 : * Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
5202 : * note gets bigger.
5203 : */
5204 382 : if (!SetSrcNoteOffset(cx, bce, noteIndex2, 0, beq - top))
5205 0 : return false;
5206 382 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, 1 + (off - top)))
5207 0 : return false;
5208 :
5209 382 : return PopStatementBCE(cx, bce);
5210 : }
5211 :
5212 : static bool
5213 51480 : EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5214 : {
5215 : /*
5216 : * Minimize bytecodes issued for one or more iterations by jumping to
5217 : * the condition below the body and closing the loop if the condition
5218 : * is true with a backward branch. For iteration count i:
5219 : *
5220 : * i test at the top test at the bottom
5221 : * = =============== ==================
5222 : * 0 ifeq-pass goto; ifne-fail
5223 : * 1 ifeq-fail; goto; ifne-pass goto; ifne-pass; ifne-fail
5224 : * 2 2*(ifeq-fail; goto); ifeq-pass goto; 2*ifne-pass; ifne-fail
5225 : * . . .
5226 : * N N*(ifeq-fail; goto); ifeq-pass goto; N*ifne-pass; ifne-fail
5227 : */
5228 : StmtInfo stmtInfo;
5229 51480 : PushStatement(bce, &stmtInfo, STMT_WHILE_LOOP, top);
5230 :
5231 51480 : ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
5232 51480 : if (noteIndex < 0)
5233 0 : return false;
5234 :
5235 51480 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
5236 51480 : if (jmp < 0)
5237 0 : return false;
5238 :
5239 51480 : top = EmitLoopHead(cx, bce, pn->pn_right);
5240 51480 : if (top < 0)
5241 0 : return false;
5242 :
5243 51480 : if (!EmitTree(cx, bce, pn->pn_right))
5244 0 : return false;
5245 :
5246 51480 : SetJumpOffsetAt(bce, jmp);
5247 51480 : if (!EmitLoopEntry(cx, bce, pn->pn_left))
5248 0 : return false;
5249 51480 : if (!EmitTree(cx, bce, pn->pn_left))
5250 0 : return false;
5251 :
5252 51480 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
5253 51480 : if (beq < 0)
5254 0 : return false;
5255 :
5256 51480 : if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
5257 0 : return false;
5258 :
5259 51480 : return PopStatementBCE(cx, bce);
5260 : }
5261 :
5262 : static bool
5263 52971 : EmitBreak(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
5264 : {
5265 52971 : StmtInfo *stmt = bce->topStmt;
5266 : SrcNoteType noteType;
5267 : jsatomid labelIndex;
5268 52971 : if (label) {
5269 90 : if (!bce->makeAtomIndex(label, &labelIndex))
5270 0 : return false;
5271 :
5272 594 : while (stmt->type != STMT_LABEL || stmt->label != label)
5273 414 : stmt = stmt->down;
5274 90 : noteType = SRC_BREAK2LABEL;
5275 : } else {
5276 52881 : labelIndex = INVALID_ATOMID;
5277 181238 : while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
5278 75476 : stmt = stmt->down;
5279 52881 : noteType = (stmt->type == STMT_SWITCH) ? SRC_SWITCHBREAK : SRC_BREAK;
5280 : }
5281 :
5282 52971 : return EmitGoto(cx, bce, stmt, &stmt->breaks, labelIndex, noteType) >= 0;
5283 : }
5284 :
5285 : static bool
5286 38613 : EmitContinue(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
5287 : {
5288 38613 : StmtInfo *stmt = bce->topStmt;
5289 : SrcNoteType noteType;
5290 : jsatomid labelIndex;
5291 38613 : if (label) {
5292 46 : if (!bce->makeAtomIndex(label, &labelIndex))
5293 0 : return false;
5294 :
5295 : /* Find the loop statement enclosed by the matching label. */
5296 46 : StmtInfo *loop = NULL;
5297 301 : while (stmt->type != STMT_LABEL || stmt->label != label) {
5298 209 : if (STMT_IS_LOOP(stmt))
5299 77 : loop = stmt;
5300 209 : stmt = stmt->down;
5301 : }
5302 46 : stmt = loop;
5303 46 : noteType = SRC_CONT2LABEL;
5304 : } else {
5305 38567 : labelIndex = INVALID_ATOMID;
5306 236177 : while (!STMT_IS_LOOP(stmt))
5307 159043 : stmt = stmt->down;
5308 38567 : noteType = SRC_CONTINUE;
5309 : }
5310 :
5311 38613 : return EmitGoto(cx, bce, stmt, &stmt->continues, labelIndex, noteType) >= 0;
5312 : }
5313 :
5314 : static bool
5315 658987 : EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5316 : {
5317 : /* Push a return value */
5318 658987 : if (ParseNode *pn2 = pn->pn_kid) {
5319 547324 : if (!EmitTree(cx, bce, pn2))
5320 0 : return false;
5321 : } else {
5322 : /* No explicit return value provided */
5323 111663 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
5324 0 : return false;
5325 : }
5326 :
5327 : /*
5328 : * EmitNonLocalJumpFixup may add fixup bytecode to close open try
5329 : * blocks having finally clauses and to exit intermingled let blocks.
5330 : * We can't simply transfer control flow to our caller in that case,
5331 : * because we must gosub to those finally clauses from inner to outer,
5332 : * with the correct stack pointer (i.e., after popping any with,
5333 : * for/in, etc., slots nested inside the finally's try).
5334 : *
5335 : * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
5336 : * extra JSOP_RETRVAL after the fixups.
5337 : */
5338 658987 : ptrdiff_t top = bce->offset();
5339 :
5340 658987 : if (Emit1(cx, bce, JSOP_RETURN) < 0)
5341 0 : return false;
5342 658987 : if (!EmitNonLocalJumpFixup(cx, bce, NULL))
5343 0 : return false;
5344 658987 : if (top + JSOP_RETURN_LENGTH != bce->offset()) {
5345 73518 : bce->base()[top] = JSOP_SETRVAL;
5346 73518 : if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
5347 0 : return false;
5348 : }
5349 :
5350 658987 : return true;
5351 : }
5352 :
5353 : static bool
5354 2395767 : EmitStatementList(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5355 : {
5356 2395767 : JS_ASSERT(pn->isArity(PN_LIST));
5357 :
5358 2395767 : ptrdiff_t noteIndex = -1;
5359 2395767 : ptrdiff_t tmp = bce->offset();
5360 2395767 : if (pn->pn_xflags & PNX_NEEDBRACES) {
5361 102 : noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0);
5362 102 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
5363 0 : return false;
5364 : }
5365 :
5366 : StmtInfo stmtInfo;
5367 2395767 : PushStatement(bce, &stmtInfo, STMT_BLOCK, top);
5368 :
5369 2395767 : ParseNode *pnchild = pn->pn_head;
5370 2395767 : if (pn->pn_xflags & PNX_FUNCDEFS) {
5371 : /*
5372 : * This block contains top-level function definitions. To ensure
5373 : * that we emit the bytecode defining them before the rest of code
5374 : * in the block we use a separate pass over functions. During the
5375 : * main pass later the emitter will add JSOP_NOP with source notes
5376 : * for the function to preserve the original functions position
5377 : * when decompiling.
5378 : *
5379 : * Currently this is used only for functions, as compile-as-we go
5380 : * mode for scripts does not allow separate emitter passes.
5381 : */
5382 27931 : JS_ASSERT(bce->inFunction());
5383 27931 : if (pn->pn_xflags & PNX_DESTRUCT) {
5384 : /*
5385 : * Assign the destructuring arguments before defining any
5386 : * functions, see bug 419662.
5387 : */
5388 4 : JS_ASSERT(pnchild->isKind(PNK_SEMI));
5389 4 : JS_ASSERT(pnchild->pn_kid->isKind(PNK_VAR) || pnchild->pn_kid->isKind(PNK_CONST));
5390 4 : if (!EmitTree(cx, bce, pnchild))
5391 0 : return false;
5392 4 : pnchild = pnchild->pn_next;
5393 : }
5394 :
5395 308047 : for (ParseNode *pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
5396 280116 : if (pn2->isKind(PNK_FUNCTION)) {
5397 40925 : if (pn2->isOp(JSOP_NOP)) {
5398 40925 : if (!EmitTree(cx, bce, pn2))
5399 0 : return false;
5400 : } else {
5401 : /*
5402 : * JSOP_DEFFUN in a top-level block with function
5403 : * definitions appears, for example, when "if (true)"
5404 : * is optimized away from "if (true) function x() {}".
5405 : * See bug 428424.
5406 : */
5407 0 : JS_ASSERT(pn2->isOp(JSOP_DEFFUN));
5408 : }
5409 : }
5410 : }
5411 : }
5412 :
5413 11205454 : for (ParseNode *pn2 = pnchild; pn2; pn2 = pn2->pn_next) {
5414 8809687 : if (!EmitTree(cx, bce, pn2))
5415 0 : return false;
5416 : }
5417 :
5418 2395767 : if (noteIndex >= 0 && !SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, bce->offset() - tmp))
5419 0 : return false;
5420 :
5421 2395767 : return PopStatementBCE(cx, bce);
5422 : }
5423 :
5424 : static bool
5425 6459839 : EmitStatement(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5426 : {
5427 6459839 : JS_ASSERT(pn->isKind(PNK_SEMI));
5428 :
5429 6459839 : ParseNode *pn2 = pn->pn_kid;
5430 6459839 : if (!pn2)
5431 4829 : return true;
5432 :
5433 : /*
5434 : * Top-level or called-from-a-native JS_Execute/EvaluateScript,
5435 : * debugger, and eval frames may need the value of the ultimate
5436 : * expression statement as the script's result, despite the fact
5437 : * that it appears useless to the compiler.
5438 : *
5439 : * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
5440 : * calling JS_Compile* to suppress JSOP_POPV.
5441 : */
5442 : bool wantval;
5443 6455010 : JSBool useful = wantval = !(bce->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
5444 :
5445 : /* Don't eliminate expressions with side effects. */
5446 6455010 : if (!useful) {
5447 5761556 : if (!CheckSideEffects(cx, bce, pn2, &useful))
5448 0 : return false;
5449 :
5450 : /*
5451 : * Don't eliminate apparently useless expressions if they are
5452 : * labeled expression statements. The tc->topStmt->update test
5453 : * catches the case where we are nesting in EmitTree for a labeled
5454 : * compound statement.
5455 : */
5456 5761583 : if (bce->topStmt &&
5457 : bce->topStmt->type == STMT_LABEL &&
5458 27 : bce->topStmt->update >= bce->offset())
5459 : {
5460 27 : useful = true;
5461 : }
5462 : }
5463 :
5464 6455010 : if (useful) {
5465 6451548 : JSOp op = wantval ? JSOP_POPV : JSOP_POP;
5466 6451548 : JS_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP));
5467 : #if JS_HAS_DESTRUCTURING
5468 14499885 : if (!wantval &&
5469 5758094 : pn2->isKind(PNK_ASSIGN) &&
5470 2290243 : !MaybeEmitGroupAssignment(cx, bce, op, pn2, &op))
5471 : {
5472 0 : return false;
5473 : }
5474 : #endif
5475 6451548 : if (op != JSOP_NOP) {
5476 : /*
5477 : * Specialize JSOP_SETPROP to JSOP_SETMETHOD to defer or
5478 : * avoid null closure cloning. Do this only for assignment
5479 : * statements that are not completion values wanted by a
5480 : * script evaluator, to ensure that the joined function
5481 : * can't escape directly.
5482 : */
5483 15113998 : if (!wantval &&
5484 5757773 : pn2->isKind(PNK_ASSIGN) &&
5485 2289922 : pn2->pn_left->isOp(JSOP_SETPROP) &&
5486 603850 : pn2->pn_right->isOp(JSOP_LAMBDA) &&
5487 11226 : pn2->pn_right->pn_funbox->joinable())
5488 : {
5489 4554 : if (!SetMethodFunction(cx, pn2->pn_right->pn_funbox, pn2->pn_left->pn_atom))
5490 0 : return false;
5491 4554 : pn2->pn_left->setOp(JSOP_SETMETHOD);
5492 : }
5493 6451227 : if (!EmitTree(cx, bce, pn2))
5494 0 : return false;
5495 6451227 : if (Emit1(cx, bce, op) < 0)
5496 0 : return false;
5497 : }
5498 3462 : } else if (!pn->isDirectivePrologueMember()) {
5499 : /* Don't complain about directive prologue members; just don't emit their code. */
5500 704 : bce->current->currentLine = pn2->pn_pos.begin.lineno;
5501 704 : if (!ReportCompileErrorNumber(cx, bce->tokenStream(), pn2,
5502 704 : JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_USELESS_EXPR))
5503 : {
5504 0 : return false;
5505 : }
5506 : }
5507 :
5508 6455010 : return true;
5509 : }
5510 :
5511 : static bool
5512 37438 : EmitDelete(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5513 : {
5514 : /*
5515 : * Under ECMA 3, deleting a non-reference returns true -- but alas we
5516 : * must evaluate the operand if it appears it might have side effects.
5517 : */
5518 37438 : ParseNode *pn2 = pn->pn_kid;
5519 37438 : switch (pn2->getKind()) {
5520 : case PNK_NAME:
5521 : {
5522 491 : if (!BindNameToSlot(cx, bce, pn2))
5523 0 : return false;
5524 491 : JSOp op = pn2->getOp();
5525 491 : if (op == JSOP_FALSE) {
5526 18 : if (Emit1(cx, bce, op) < 0)
5527 0 : return false;
5528 : } else {
5529 473 : if (!EmitAtomOp(cx, pn2, op, bce))
5530 0 : return false;
5531 : }
5532 491 : break;
5533 : }
5534 : case PNK_DOT:
5535 22872 : if (!EmitPropOp(cx, pn2, JSOP_DELPROP, bce, false))
5536 0 : return false;
5537 22872 : break;
5538 : #if JS_HAS_XML_SUPPORT
5539 : case PNK_DBLDOT:
5540 0 : JS_ASSERT(!bce->inStrictMode());
5541 0 : if (!EmitElemOp(cx, pn2, JSOP_DELDESC, bce))
5542 0 : return false;
5543 0 : break;
5544 : #endif
5545 : case PNK_LB:
5546 14075 : if (!EmitElemOp(cx, pn2, JSOP_DELELEM, bce))
5547 0 : return false;
5548 14075 : break;
5549 : default:
5550 : {
5551 : /*
5552 : * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
5553 : * to foo(), true (a comma expression, requiring SRC_PCDELTA).
5554 : */
5555 0 : JSBool useful = false;
5556 0 : if (!CheckSideEffects(cx, bce, pn2, &useful))
5557 0 : return false;
5558 :
5559 : ptrdiff_t off, noteIndex;
5560 0 : if (useful) {
5561 0 : JS_ASSERT_IF(pn2->isKind(PNK_LP), !(pn2->pn_xflags & PNX_SETCALL));
5562 0 : if (!EmitTree(cx, bce, pn2))
5563 0 : return false;
5564 0 : off = bce->offset();
5565 0 : noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
5566 0 : if (noteIndex < 0 || Emit1(cx, bce, JSOP_POP) < 0)
5567 0 : return false;
5568 : } else {
5569 0 : off = noteIndex = -1;
5570 : }
5571 :
5572 0 : if (Emit1(cx, bce, JSOP_TRUE) < 0)
5573 0 : return false;
5574 0 : if (noteIndex >= 0) {
5575 0 : ptrdiff_t tmp = bce->offset();
5576 0 : if (!SetSrcNoteOffset(cx, bce, unsigned(noteIndex), 0, tmp - off))
5577 0 : return false;
5578 : }
5579 : }
5580 : }
5581 :
5582 37438 : return true;
5583 : }
5584 :
5585 : static bool
5586 3325217 : EmitCallOrNew(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5587 : {
5588 3325217 : bool callop = pn->isKind(PNK_LP);
5589 :
5590 : /*
5591 : * Emit callable invocation or operator new (constructor call) code.
5592 : * First, emit code for the left operand to evaluate the callable or
5593 : * constructable object expression.
5594 : *
5595 : * For operator new applied to other expressions than E4X ones, we emit
5596 : * JSOP_GETPROP instead of JSOP_CALLPROP, etc. This is necessary to
5597 : * interpose the lambda-initialized method read barrier -- see the code
5598 : * in jsinterp.cpp for JSOP_LAMBDA followed by JSOP_{SET,INIT}PROP.
5599 : *
5600 : * Then (or in a call case that has no explicit reference-base
5601 : * object) we emit JSOP_UNDEFINED to produce the undefined |this|
5602 : * value required for calls (which non-strict mode functions
5603 : * will box into the global object).
5604 : */
5605 3325217 : ParseNode *pn2 = pn->pn_head;
5606 3325217 : switch (pn2->getKind()) {
5607 : case PNK_NAME:
5608 1001380 : if (!EmitNameOp(cx, bce, pn2, callop))
5609 0 : return false;
5610 1001380 : break;
5611 : case PNK_DOT:
5612 2309881 : if (!EmitPropOp(cx, pn2, pn2->getOp(), bce, callop))
5613 0 : return false;
5614 2309881 : break;
5615 : case PNK_LB:
5616 4720 : JS_ASSERT(pn2->isOp(JSOP_GETELEM));
5617 4720 : if (!EmitElemOp(cx, pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM, bce))
5618 0 : return false;
5619 4720 : break;
5620 : #if JS_HAS_XML_SUPPORT
5621 : case PNK_XMLUNARY:
5622 9 : JS_ASSERT(pn2->isOp(JSOP_XMLNAME));
5623 9 : if (!EmitXMLName(cx, pn2, JSOP_CALLXMLNAME, bce))
5624 0 : return false;
5625 9 : callop = true; /* suppress JSOP_UNDEFINED after */
5626 9 : break;
5627 : #endif
5628 : default:
5629 9227 : if (!EmitTree(cx, bce, pn2))
5630 0 : return false;
5631 9227 : callop = false; /* trigger JSOP_UNDEFINED after */
5632 9227 : break;
5633 : }
5634 3325217 : if (!callop && Emit1(cx, bce, JSOP_UNDEFINED) < 0)
5635 0 : return false;
5636 :
5637 : /* Remember start of callable-object bytecode for decompilation hint. */
5638 3325217 : ptrdiff_t off = top;
5639 :
5640 : /*
5641 : * Emit code for each argument in order, then emit the JSOP_*CALL or
5642 : * JSOP_NEW bytecode with a two-byte immediate telling how many args
5643 : * were pushed on the operand stack.
5644 : */
5645 3325217 : unsigned oldflags = bce->flags;
5646 3325217 : bce->flags &= ~TCF_IN_FOR_INIT;
5647 7681045 : for (ParseNode *pn3 = pn2->pn_next; pn3; pn3 = pn3->pn_next) {
5648 4355828 : if (!EmitTree(cx, bce, pn3))
5649 0 : return false;
5650 : }
5651 3325217 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
5652 3325217 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - off) < 0)
5653 0 : return false;
5654 :
5655 3325217 : uint32_t argc = pn->pn_count - 1;
5656 3325217 : if (Emit3(cx, bce, pn->getOp(), ARGC_HI(argc), ARGC_LO(argc)) < 0)
5657 0 : return false;
5658 3325217 : CheckTypeSet(cx, bce, pn->getOp());
5659 3325217 : if (pn->isOp(JSOP_EVAL))
5660 4783 : EMIT_UINT16_IMM_OP(JSOP_LINENO, pn->pn_pos.begin.lineno);
5661 3325217 : if (pn->pn_xflags & PNX_SETCALL) {
5662 27 : if (Emit1(cx, bce, JSOP_SETCALL) < 0)
5663 0 : return false;
5664 : }
5665 3325217 : return true;
5666 : }
5667 :
5668 : static bool
5669 229254 : EmitLogical(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5670 : {
5671 : /*
5672 : * JSOP_OR converts the operand on the stack to boolean, leaves the original
5673 : * value on the stack and jumps if true; otherwise it falls into the next
5674 : * bytecode, which pops the left operand and then evaluates the right operand.
5675 : * The jump goes around the right operand evaluation.
5676 : *
5677 : * JSOP_AND converts the operand on the stack to boolean and jumps if false;
5678 : * otherwise it falls into the right operand's bytecode.
5679 : */
5680 :
5681 229254 : if (pn->isArity(PN_BINARY)) {
5682 196764 : if (!EmitTree(cx, bce, pn->pn_left))
5683 0 : return false;
5684 196764 : ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
5685 196764 : if (top < 0)
5686 0 : return false;
5687 196764 : if (Emit1(cx, bce, JSOP_POP) < 0)
5688 0 : return false;
5689 196764 : if (!EmitTree(cx, bce, pn->pn_right))
5690 0 : return false;
5691 196764 : ptrdiff_t off = bce->offset();
5692 196764 : jsbytecode *pc = bce->code(top);
5693 196764 : SET_JUMP_OFFSET(pc, off - top);
5694 196764 : *pc = pn->getOp();
5695 196764 : return true;
5696 : }
5697 :
5698 32490 : JS_ASSERT(pn->isArity(PN_LIST));
5699 32490 : JS_ASSERT(pn->pn_head->pn_next->pn_next);
5700 :
5701 : /* Left-associative operator chain: avoid too much recursion. */
5702 32490 : ParseNode *pn2 = pn->pn_head;
5703 32490 : if (!EmitTree(cx, bce, pn2))
5704 0 : return false;
5705 32490 : ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
5706 32490 : if (top < 0)
5707 0 : return false;
5708 32490 : if (Emit1(cx, bce, JSOP_POP) < 0)
5709 0 : return false;
5710 :
5711 : /* Emit nodes between the head and the tail. */
5712 32490 : ptrdiff_t jmp = top;
5713 107951 : while ((pn2 = pn2->pn_next)->pn_next) {
5714 42971 : if (!EmitTree(cx, bce, pn2))
5715 0 : return false;
5716 42971 : ptrdiff_t off = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
5717 42971 : if (off < 0)
5718 0 : return false;
5719 42971 : if (Emit1(cx, bce, JSOP_POP) < 0)
5720 0 : return false;
5721 42971 : SET_JUMP_OFFSET(bce->code(jmp), off - jmp);
5722 42971 : jmp = off;
5723 : }
5724 32490 : if (!EmitTree(cx, bce, pn2))
5725 0 : return false;
5726 :
5727 32490 : pn2 = pn->pn_head;
5728 32490 : ptrdiff_t off = bce->offset();
5729 75461 : do {
5730 75461 : jsbytecode *pc = bce->code(top);
5731 75461 : ptrdiff_t tmp = GET_JUMP_OFFSET(pc);
5732 75461 : SET_JUMP_OFFSET(pc, off - top);
5733 75461 : *pc = pn->getOp();
5734 75461 : top += tmp;
5735 : } while ((pn2 = pn2->pn_next)->pn_next);
5736 :
5737 32490 : return true;
5738 : }
5739 :
5740 : static bool
5741 1732196 : EmitIncOrDec(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5742 : {
5743 : /* Emit lvalue-specialized code for ++/-- operators. */
5744 1732196 : ParseNode *pn2 = pn->pn_kid;
5745 1732196 : JS_ASSERT(!pn2->isKind(PNK_RP));
5746 1732196 : JSOp op = pn->getOp();
5747 1732196 : switch (pn2->getKind()) {
5748 : case PNK_DOT:
5749 4863 : if (!EmitPropIncDec(cx, pn2, op, bce))
5750 0 : return false;
5751 4863 : break;
5752 : case PNK_LB:
5753 1634112 : if (!EmitElemIncDec(cx, pn2, op, bce))
5754 0 : return false;
5755 1634112 : break;
5756 : case PNK_LP:
5757 9 : if (!EmitTree(cx, bce, pn2))
5758 0 : return false;
5759 9 : if (NewSrcNote2(cx, bce, SRC_PCBASE, bce->offset() - pn2->pn_offset) < 0)
5760 0 : return false;
5761 9 : if (Emit1(cx, bce, op) < 0)
5762 0 : return false;
5763 : /*
5764 : * This is dead code for the decompiler, don't generate
5765 : * a decomposed version of the opcode. We do need to balance
5766 : * the stacks in the decomposed version.
5767 : */
5768 9 : JS_ASSERT(js_CodeSpec[op].format & JOF_DECOMPOSE);
5769 9 : JS_ASSERT(js_CodeSpec[op].format & JOF_ELEM);
5770 9 : if (Emit1(cx, bce, (JSOp)1) < 0)
5771 0 : return false;
5772 9 : if (Emit1(cx, bce, JSOP_POP) < 0)
5773 0 : return false;
5774 9 : break;
5775 : #if JS_HAS_XML_SUPPORT
5776 : case PNK_XMLUNARY:
5777 9 : JS_ASSERT(!bce->inStrictMode());
5778 9 : JS_ASSERT(pn2->isOp(JSOP_SETXMLNAME));
5779 9 : if (!EmitTree(cx, bce, pn2->pn_kid))
5780 0 : return false;
5781 9 : if (Emit1(cx, bce, JSOP_BINDXMLNAME) < 0)
5782 0 : return false;
5783 9 : if (!EmitElemIncDec(cx, NULL, op, bce))
5784 0 : return false;
5785 9 : break;
5786 : #endif
5787 : default:
5788 93203 : JS_ASSERT(pn2->isKind(PNK_NAME));
5789 93203 : pn2->setOp(op);
5790 93203 : if (!BindNameToSlot(cx, bce, pn2))
5791 0 : return false;
5792 93203 : op = pn2->getOp();
5793 93203 : if (op == JSOP_CALLEE) {
5794 0 : if (Emit1(cx, bce, op) < 0)
5795 0 : return false;
5796 93203 : } else if (!pn2->pn_cookie.isFree()) {
5797 70591 : jsatomid atomIndex = pn2->pn_cookie.asInteger();
5798 70591 : EMIT_UINT16_IMM_OP(op, atomIndex);
5799 : } else {
5800 22612 : JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
5801 22612 : if (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)) {
5802 22603 : if (!EmitNameIncDec(cx, pn2, op, bce))
5803 0 : return false;
5804 : } else {
5805 9 : if (!EmitAtomOp(cx, pn2, op, bce))
5806 0 : return false;
5807 : }
5808 22612 : break;
5809 : }
5810 70591 : if (pn2->isConst()) {
5811 0 : if (Emit1(cx, bce, JSOP_POS) < 0)
5812 0 : return false;
5813 0 : op = pn->getOp();
5814 0 : if (!(js_CodeSpec[op].format & JOF_POST)) {
5815 0 : if (Emit1(cx, bce, JSOP_ONE) < 0)
5816 0 : return false;
5817 0 : op = (js_CodeSpec[op].format & JOF_INC) ? JSOP_ADD : JSOP_SUB;
5818 0 : if (Emit1(cx, bce, op) < 0)
5819 0 : return false;
5820 : }
5821 : }
5822 : }
5823 1732196 : return true;
5824 : }
5825 :
5826 : /*
5827 : * Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr12127. See
5828 : * the comment on EmitSwitch.
5829 : */
5830 : MOZ_NEVER_INLINE static bool
5831 193 : EmitLabel(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5832 : {
5833 : /*
5834 : * Emit a JSOP_LABEL instruction. The argument is the offset to the statement
5835 : * following the labeled statement. This op has either a SRC_LABEL or
5836 : * SRC_LABELBRACE source note for the decompiler.
5837 : */
5838 193 : JSAtom *atom = pn->pn_atom;
5839 :
5840 : jsatomid index;
5841 193 : if (!bce->makeAtomIndex(atom, &index))
5842 0 : return false;
5843 :
5844 193 : ParseNode *pn2 = pn->expr();
5845 193 : SrcNoteType noteType = (pn2->isKind(PNK_STATEMENTLIST) ||
5846 193 : (pn2->isKind(PNK_LEXICALSCOPE) &&
5847 0 : pn2->expr()->isKind(PNK_STATEMENTLIST)))
5848 : ? SRC_LABELBRACE
5849 386 : : SRC_LABEL;
5850 193 : ptrdiff_t noteIndex = NewSrcNote2(cx, bce, noteType, ptrdiff_t(index));
5851 193 : if (noteIndex < 0)
5852 0 : return false;
5853 :
5854 193 : ptrdiff_t top = EmitJump(cx, bce, JSOP_LABEL, 0);
5855 193 : if (top < 0)
5856 0 : return false;
5857 :
5858 : /* Emit code for the labeled statement. */
5859 : StmtInfo stmtInfo;
5860 193 : PushStatement(bce, &stmtInfo, STMT_LABEL, bce->offset());
5861 193 : stmtInfo.label = atom;
5862 193 : if (!EmitTree(cx, bce, pn2))
5863 0 : return false;
5864 193 : if (!PopStatementBCE(cx, bce))
5865 0 : return false;
5866 :
5867 : /* Patch the JSOP_LABEL offset. */
5868 193 : SetJumpOffsetAt(bce, top);
5869 :
5870 : /* If the statement was compound, emit a note for the end brace. */
5871 193 : if (noteType == SRC_LABELBRACE) {
5872 0 : if (NewSrcNote(cx, bce, SRC_ENDBRACE) < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
5873 0 : return false;
5874 : }
5875 :
5876 193 : return true;
5877 : }
5878 :
5879 : static bool
5880 931 : EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
5881 : {
5882 931 : JS_ASSERT(pn->isArity(PN_LIST));
5883 : StmtInfo stmtInfo;
5884 931 : PushStatement(bce, &stmtInfo, STMT_SEQ, top);
5885 2793 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5886 1862 : if (!EmitTree(cx, bce, pn2))
5887 0 : return false;
5888 : }
5889 931 : return PopStatementBCE(cx, bce);
5890 : }
5891 :
5892 : static bool
5893 68229 : EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
5894 : {
5895 : /* Emit the condition, then branch if false to the else part. */
5896 68229 : if (!EmitTree(cx, bce, &conditional.condition()))
5897 0 : return false;
5898 68229 : ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
5899 68229 : if (noteIndex < 0)
5900 0 : return false;
5901 68229 : ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
5902 68229 : if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression()))
5903 0 : return false;
5904 :
5905 : /* Jump around else, fixup the branch, emit else, fixup jump. */
5906 68229 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
5907 68229 : if (jmp < 0)
5908 0 : return false;
5909 68229 : SetJumpOffsetAt(bce, beq);
5910 :
5911 : /*
5912 : * Because each branch pushes a single value, but our stack budgeting
5913 : * analysis ignores branches, we now have to adjust bce->stackDepth to
5914 : * ignore the value pushed by the first branch. Execution will follow
5915 : * only one path, so we must decrement bce->stackDepth.
5916 : *
5917 : * Failing to do this will foil code, such as let expression and block
5918 : * code generation, which must use the stack depth to compute local
5919 : * stack indexes correctly.
5920 : */
5921 68229 : JS_ASSERT(bce->stackDepth > 0);
5922 68229 : bce->stackDepth--;
5923 68229 : if (!EmitTree(cx, bce, &conditional.elseExpression()))
5924 0 : return false;
5925 68229 : SetJumpOffsetAt(bce, jmp);
5926 68229 : return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
5927 : }
5928 :
5929 : static bool
5930 216046 : EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
5931 : {
5932 : #if JS_HAS_DESTRUCTURING_SHORTHAND
5933 216046 : if (pn->pn_xflags & PNX_DESTRUCT) {
5934 : ReportCompileErrorNumber(cx, bce->tokenStream(), pn, JSREPORT_ERROR,
5935 0 : JSMSG_BAD_OBJECT_INIT);
5936 0 : return false;
5937 : }
5938 : #endif
5939 :
5940 216046 : if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
5941 1842 : return EmitSingletonInitialiser(cx, bce, pn);
5942 :
5943 : /*
5944 : * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
5945 : * a new object and in source order evaluating each property value and
5946 : * adding the property to the object, without invoking latent setters.
5947 : * We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to
5948 : * ignore setters and to avoid dup'ing and popping the object as each
5949 : * property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
5950 : */
5951 214204 : ptrdiff_t offset = bce->next() - bce->base();
5952 214204 : if (!EmitNewInit(cx, bce, JSProto_Object, pn))
5953 0 : return false;
5954 :
5955 : /*
5956 : * Try to construct the shape of the object as we go, so we can emit a
5957 : * JSOP_NEWOBJECT with the final shape instead.
5958 : */
5959 214204 : JSObject *obj = NULL;
5960 214204 : if (bce->compileAndGo()) {
5961 9845 : gc::AllocKind kind = GuessObjectGCKind(pn->pn_count);
5962 9845 : obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
5963 9845 : if (!obj)
5964 0 : return false;
5965 : }
5966 :
5967 214204 : unsigned methodInits = 0, slowMethodInits = 0;
5968 1082546 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
5969 : /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
5970 868342 : ParseNode *pn3 = pn2->pn_left;
5971 868342 : if (pn3->isKind(PNK_NUMBER)) {
5972 4198 : if (!EmitNumberOp(cx, pn3->pn_dval, bce))
5973 0 : return false;
5974 : }
5975 :
5976 : /* Emit code for the property initializer. */
5977 868342 : if (!EmitTree(cx, bce, pn2->pn_right))
5978 0 : return false;
5979 :
5980 868342 : JSOp op = pn2->getOp();
5981 868342 : if (op == JSOP_GETTER || op == JSOP_SETTER) {
5982 67486 : obj = NULL;
5983 67486 : if (Emit1(cx, bce, op) < 0)
5984 0 : return false;
5985 : }
5986 :
5987 : /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
5988 868342 : if (pn3->isKind(PNK_NUMBER)) {
5989 4198 : obj = NULL;
5990 4198 : if (NewSrcNote(cx, bce, SRC_INITPROP) < 0)
5991 0 : return false;
5992 4198 : if (Emit1(cx, bce, JSOP_INITELEM) < 0)
5993 0 : return false;
5994 : } else {
5995 864144 : JS_ASSERT(pn3->isKind(PNK_NAME) || pn3->isKind(PNK_STRING));
5996 : jsatomid index;
5997 864144 : if (!bce->makeAtomIndex(pn3->pn_atom, &index))
5998 0 : return false;
5999 :
6000 : /* Check whether we can optimize to JSOP_INITMETHOD. */
6001 864144 : ParseNode *init = pn2->pn_right;
6002 864144 : bool lambda = init->isOp(JSOP_LAMBDA);
6003 864144 : if (lambda)
6004 461215 : ++methodInits;
6005 864144 : if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable()) {
6006 2542 : obj = NULL;
6007 2542 : op = JSOP_INITMETHOD;
6008 2542 : if (!SetMethodFunction(cx, init->pn_funbox, pn3->pn_atom))
6009 0 : return JS_FALSE;
6010 2542 : pn2->setOp(op);
6011 : } else {
6012 : /*
6013 : * Disable NEWOBJECT on initializers that set __proto__, which has
6014 : * a non-standard setter on objects.
6015 : */
6016 861602 : if (pn3->pn_atom == cx->runtime->atomState.protoAtom)
6017 9771 : obj = NULL;
6018 861602 : op = JSOP_INITPROP;
6019 861602 : if (lambda)
6020 458673 : ++slowMethodInits;
6021 : }
6022 :
6023 864144 : if (obj) {
6024 8909 : JS_ASSERT(!obj->inDictionaryMode());
6025 17818 : if (!DefineNativeProperty(cx, obj, ATOM_TO_JSID(pn3->pn_atom),
6026 : UndefinedValue(), NULL, NULL,
6027 17818 : JSPROP_ENUMERATE, 0, 0))
6028 : {
6029 0 : return false;
6030 : }
6031 8909 : if (obj->inDictionaryMode())
6032 9 : obj = NULL;
6033 : }
6034 :
6035 864144 : if (!EmitIndex32(cx, op, index, bce))
6036 0 : return false;
6037 : }
6038 : }
6039 :
6040 214204 : if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
6041 0 : return false;
6042 :
6043 214204 : if (obj) {
6044 : /*
6045 : * The object survived and has a predictable shape: update the original
6046 : * bytecode.
6047 : */
6048 7553 : ObjectBox *objbox = bce->parser->newObjectBox(obj);
6049 7553 : if (!objbox)
6050 0 : return false;
6051 7553 : unsigned index = bce->objectList.index(objbox);
6052 : MOZ_STATIC_ASSERT(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
6053 : "newinit and newobject must have equal length to edit in-place");
6054 7553 : EMIT_UINT32_IN_PLACE(offset, JSOP_NEWOBJECT, uint32_t(index));
6055 : }
6056 :
6057 214204 : return true;
6058 : }
6059 :
6060 : static bool
6061 223948 : EmitArray(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
6062 : {
6063 : /*
6064 : * Emit code for [a, b, c] that is equivalent to constructing a new
6065 : * array and in source order evaluating each element value and adding
6066 : * it to the array, without invoking latent setters. We use the
6067 : * JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to
6068 : * avoid dup'ing and popping the array as each element is added, as
6069 : * JSOP_SETELEM/JSOP_SETPROP would do.
6070 : */
6071 :
6072 : #if JS_HAS_GENERATORS
6073 223948 : if (pn->isKind(PNK_ARRAYCOMP)) {
6074 12670 : if (!EmitNewInit(cx, bce, JSProto_Array, pn))
6075 0 : return false;
6076 :
6077 : /*
6078 : * Pass the new array's stack index to the PNK_ARRAYPUSH case via
6079 : * bce->arrayCompDepth, then simply traverse the PNK_FOR node and
6080 : * its kids under pn2 to generate this comprehension.
6081 : */
6082 12670 : JS_ASSERT(bce->stackDepth > 0);
6083 12670 : unsigned saveDepth = bce->arrayCompDepth;
6084 12670 : bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
6085 12670 : if (!EmitTree(cx, bce, pn->pn_head))
6086 0 : return false;
6087 12670 : bce->arrayCompDepth = saveDepth;
6088 :
6089 : /* Emit the usual op needed for decompilation. */
6090 12670 : return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
6091 : }
6092 : #endif /* JS_HAS_GENERATORS */
6093 :
6094 211278 : if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
6095 5412 : return EmitSingletonInitialiser(cx, bce, pn);
6096 :
6097 205866 : ptrdiff_t off = EmitN(cx, bce, JSOP_NEWARRAY, 3);
6098 205866 : if (off < 0)
6099 0 : return false;
6100 205866 : CheckTypeSet(cx, bce, JSOP_NEWARRAY);
6101 205866 : jsbytecode *pc = bce->code(off);
6102 205866 : SET_UINT24(pc, pn->pn_count);
6103 :
6104 205866 : ParseNode *pn2 = pn->pn_head;
6105 : jsatomid atomIndex;
6106 5480878 : for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
6107 5275012 : if (!EmitNumberOp(cx, atomIndex, bce))
6108 0 : return false;
6109 5275012 : if (pn2->isKind(PNK_COMMA) && pn2->isArity(PN_NULLARY)) {
6110 4890 : if (Emit1(cx, bce, JSOP_HOLE) < 0)
6111 0 : return false;
6112 : } else {
6113 5270122 : if (!EmitTree(cx, bce, pn2))
6114 0 : return false;
6115 : }
6116 5275012 : if (Emit1(cx, bce, JSOP_INITELEM) < 0)
6117 0 : return false;
6118 : }
6119 205866 : JS_ASSERT(atomIndex == pn->pn_count);
6120 :
6121 205866 : if (pn->pn_xflags & PNX_ENDCOMMA) {
6122 : /* Emit a source note so we know to decompile an extra comma. */
6123 2325 : if (NewSrcNote(cx, bce, SRC_CONTINUE) < 0)
6124 0 : return false;
6125 : }
6126 :
6127 : /* Emit an op to finish the array and aid in decompilation. */
6128 205866 : return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
6129 : }
6130 :
6131 : static bool
6132 530286 : EmitUnary(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
6133 : {
6134 : /* Unary op, including unary +/-. */
6135 530286 : JSOp op = pn->getOp();
6136 530286 : ParseNode *pn2 = pn->pn_kid;
6137 :
6138 530286 : JS_ASSERT(op != JSOP_XMLNAME);
6139 530286 : if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
6140 0 : op = JSOP_TYPEOFEXPR;
6141 :
6142 530286 : unsigned oldflags = bce->flags;
6143 530286 : bce->flags &= ~TCF_IN_FOR_INIT;
6144 530286 : if (!EmitTree(cx, bce, pn2))
6145 0 : return false;
6146 :
6147 530286 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
6148 530286 : return Emit1(cx, bce, op) >= 0;
6149 : }
6150 :
6151 : JSBool
6152 59153270 : frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
6153 : {
6154 59153270 : JS_CHECK_RECURSION(cx, return JS_FALSE);
6155 :
6156 118306540 : EmitLevelManager elm(bce);
6157 :
6158 59153270 : JSBool ok = true;
6159 59153270 : ptrdiff_t top = bce->offset();
6160 59153270 : pn->pn_offset = top;
6161 :
6162 : /* Emit notes to tell the current bytecode's source line number. */
6163 59153270 : UPDATE_LINE_NUMBER_NOTES(cx, bce, pn->pn_pos.begin.lineno);
6164 :
6165 59153270 : switch (pn->getKind()) {
6166 : case PNK_FUNCTION:
6167 1017444 : ok = EmitFunc(cx, bce, pn);
6168 1017444 : break;
6169 :
6170 : case PNK_ARGSBODY:
6171 : {
6172 644823 : ParseNode *pnlast = pn->last();
6173 1848876 : for (ParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
6174 1204053 : if (!pn2->isDefn())
6175 18 : continue;
6176 1204035 : if (!BindNameToSlot(cx, bce, pn2))
6177 0 : return JS_FALSE;
6178 1204035 : if (JOF_OPTYPE(pn2->getOp()) == JOF_QARG && bce->shouldNoteClosedName(pn2)) {
6179 113907 : if (!bce->closedArgs.append(pn2->pn_cookie.slot()))
6180 0 : return JS_FALSE;
6181 : }
6182 : }
6183 644823 : ok = EmitTree(cx, bce, pnlast);
6184 644823 : break;
6185 : }
6186 :
6187 : case PNK_UPVARS:
6188 809476 : JS_ASSERT(pn->pn_names->count() != 0);
6189 809476 : bce->roLexdeps = pn->pn_names;
6190 809476 : ok = EmitTree(cx, bce, pn->pn_tree);
6191 809476 : bce->roLexdeps.clearMap();
6192 809476 : pn->pn_names.releaseMap(cx);
6193 809476 : break;
6194 :
6195 : case PNK_IF:
6196 927490 : ok = EmitIf(cx, bce, pn);
6197 927490 : break;
6198 :
6199 : case PNK_SWITCH:
6200 17196 : ok = EmitSwitch(cx, bce, pn);
6201 17196 : break;
6202 :
6203 : case PNK_WHILE:
6204 51480 : ok = EmitWhile(cx, bce, pn, top);
6205 51480 : break;
6206 :
6207 : case PNK_DOWHILE:
6208 382 : ok = EmitDo(cx, bce, pn);
6209 382 : break;
6210 :
6211 : case PNK_FOR:
6212 121998 : ok = EmitFor(cx, bce, pn, top);
6213 121998 : break;
6214 :
6215 : case PNK_BREAK:
6216 52971 : ok = EmitBreak(cx, bce, pn->asBreakStatement().label());
6217 52971 : break;
6218 :
6219 : case PNK_CONTINUE:
6220 38613 : ok = EmitContinue(cx, bce, pn->asContinueStatement().label());
6221 38613 : break;
6222 :
6223 : case PNK_WITH:
6224 630 : ok = EmitWith(cx, bce, pn);
6225 630 : break;
6226 :
6227 : case PNK_TRY:
6228 240738 : if (!EmitTry(cx, bce, pn))
6229 0 : return false;
6230 240738 : break;
6231 :
6232 : case PNK_CATCH:
6233 224906 : if (!EmitCatch(cx, bce, pn))
6234 0 : return false;
6235 224906 : break;
6236 :
6237 : case PNK_VAR:
6238 : case PNK_CONST:
6239 1022981 : if (!EmitVariables(cx, bce, pn, InitializeVars))
6240 0 : return JS_FALSE;
6241 1022981 : break;
6242 :
6243 : case PNK_RETURN:
6244 658987 : ok = EmitReturn(cx, bce, pn);
6245 658987 : break;
6246 :
6247 : #if JS_HAS_GENERATORS
6248 : case PNK_YIELD:
6249 4366 : JS_ASSERT(bce->inFunction());
6250 4366 : if (pn->pn_kid) {
6251 3585 : if (!EmitTree(cx, bce, pn->pn_kid))
6252 0 : return JS_FALSE;
6253 : } else {
6254 781 : if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
6255 0 : return JS_FALSE;
6256 : }
6257 4366 : if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
6258 0 : return JS_FALSE;
6259 4366 : if (Emit1(cx, bce, JSOP_YIELD) < 0)
6260 0 : return JS_FALSE;
6261 4366 : break;
6262 : #endif
6263 :
6264 : #if JS_HAS_XML_SUPPORT
6265 : case PNK_XMLCURLYEXPR:
6266 0 : JS_ASSERT(pn->isArity(PN_UNARY));
6267 0 : if (!EmitTree(cx, bce, pn->pn_kid))
6268 0 : return JS_FALSE;
6269 0 : if (Emit1(cx, bce, pn->getOp()) < 0)
6270 0 : return JS_FALSE;
6271 0 : break;
6272 : #endif
6273 :
6274 : case PNK_STATEMENTLIST:
6275 2395767 : ok = EmitStatementList(cx, bce, pn, top);
6276 2395767 : break;
6277 :
6278 : case PNK_SEQ:
6279 931 : ok = EmitSyntheticStatements(cx, bce, pn, top);
6280 931 : break;
6281 :
6282 : case PNK_SEMI:
6283 6459839 : ok = EmitStatement(cx, bce, pn);
6284 6459839 : break;
6285 :
6286 : case PNK_COLON:
6287 193 : ok = EmitLabel(cx, bce, pn);
6288 193 : break;
6289 :
6290 : case PNK_COMMA:
6291 : {
6292 : /*
6293 : * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
6294 : * These notes help the decompiler bracket the bytecodes generated
6295 : * from each sub-expression that follows a comma.
6296 : */
6297 2098 : ptrdiff_t off = -1, noteIndex = -1;
6298 4707 : for (ParseNode *pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
6299 4707 : if (!EmitTree(cx, bce, pn2))
6300 0 : return JS_FALSE;
6301 4707 : ptrdiff_t tmp = bce->offset();
6302 4707 : if (noteIndex >= 0) {
6303 2609 : if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, tmp-off))
6304 0 : return JS_FALSE;
6305 : }
6306 4707 : if (!pn2->pn_next)
6307 : break;
6308 2609 : off = tmp;
6309 2609 : noteIndex = NewSrcNote2(cx, bce, SRC_PCDELTA, 0);
6310 5218 : if (noteIndex < 0 ||
6311 2609 : Emit1(cx, bce, JSOP_POP) < 0) {
6312 0 : return JS_FALSE;
6313 : }
6314 : }
6315 2098 : break;
6316 : }
6317 :
6318 : case PNK_ASSIGN:
6319 : case PNK_ADDASSIGN:
6320 : case PNK_SUBASSIGN:
6321 : case PNK_BITORASSIGN:
6322 : case PNK_BITXORASSIGN:
6323 : case PNK_BITANDASSIGN:
6324 : case PNK_LSHASSIGN:
6325 : case PNK_RSHASSIGN:
6326 : case PNK_URSHASSIGN:
6327 : case PNK_MULASSIGN:
6328 : case PNK_DIVASSIGN:
6329 : case PNK_MODASSIGN:
6330 3012393 : if (!EmitAssignment(cx, bce, pn->pn_left, pn->getOp(), pn->pn_right))
6331 0 : return false;
6332 3012393 : break;
6333 :
6334 : case PNK_CONDITIONAL:
6335 68229 : ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression());
6336 68229 : break;
6337 :
6338 : case PNK_OR:
6339 : case PNK_AND:
6340 229254 : ok = EmitLogical(cx, bce, pn);
6341 229254 : break;
6342 :
6343 : case PNK_ADD:
6344 : case PNK_SUB:
6345 : case PNK_BITOR:
6346 : case PNK_BITXOR:
6347 : case PNK_BITAND:
6348 : case PNK_STRICTEQ:
6349 : case PNK_EQ:
6350 : case PNK_STRICTNE:
6351 : case PNK_NE:
6352 : case PNK_LT:
6353 : case PNK_LE:
6354 : case PNK_GT:
6355 : case PNK_GE:
6356 : case PNK_IN:
6357 : case PNK_INSTANCEOF:
6358 : case PNK_LSH:
6359 : case PNK_RSH:
6360 : case PNK_URSH:
6361 : case PNK_STAR:
6362 : case PNK_DIV:
6363 : case PNK_MOD:
6364 1120565 : if (pn->isArity(PN_LIST)) {
6365 : /* Left-associative operator chain: avoid too much recursion. */
6366 165692 : ParseNode *pn2 = pn->pn_head;
6367 165692 : if (!EmitTree(cx, bce, pn2))
6368 0 : return JS_FALSE;
6369 165692 : JSOp op = pn->getOp();
6370 165692 : while ((pn2 = pn2->pn_next) != NULL) {
6371 2043352 : if (!EmitTree(cx, bce, pn2))
6372 0 : return JS_FALSE;
6373 2043352 : if (Emit1(cx, bce, op) < 0)
6374 0 : return JS_FALSE;
6375 : }
6376 : } else {
6377 : #if JS_HAS_XML_SUPPORT
6378 : unsigned oldflags;
6379 :
6380 : case PNK_DBLCOLON:
6381 954945 : JS_ASSERT(pn->getOp() != JSOP_XMLNAME);
6382 954945 : if (pn->isArity(PN_NAME)) {
6383 72 : if (!EmitTree(cx, bce, pn->expr()))
6384 0 : return JS_FALSE;
6385 72 : if (!EmitAtomOp(cx, pn, pn->getOp(), bce))
6386 0 : return JS_FALSE;
6387 72 : break;
6388 : }
6389 :
6390 : /*
6391 : * Binary :: has a right operand that brackets arbitrary code,
6392 : * possibly including a let (a = b) ... expression. We must clear
6393 : * TCF_IN_FOR_INIT to avoid mis-compiling such beasts.
6394 : */
6395 954873 : oldflags = bce->flags;
6396 954873 : bce->flags &= ~TCF_IN_FOR_INIT;
6397 : #endif
6398 :
6399 : /* Binary operators that evaluate both operands unconditionally. */
6400 954873 : if (!EmitTree(cx, bce, pn->pn_left))
6401 0 : return JS_FALSE;
6402 954873 : if (!EmitTree(cx, bce, pn->pn_right))
6403 0 : return JS_FALSE;
6404 : #if JS_HAS_XML_SUPPORT
6405 954873 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
6406 : #endif
6407 954873 : if (Emit1(cx, bce, pn->getOp()) < 0)
6408 0 : return JS_FALSE;
6409 : }
6410 1120565 : break;
6411 :
6412 : #if JS_HAS_XML_SUPPORT
6413 : case PNK_XMLUNARY:
6414 54 : if (pn->getOp() == JSOP_XMLNAME) {
6415 54 : if (!EmitXMLName(cx, pn, JSOP_XMLNAME, bce))
6416 0 : return false;
6417 : } else {
6418 0 : JSOp op = pn->getOp();
6419 0 : JS_ASSERT(op == JSOP_BINDXMLNAME || op == JSOP_SETXMLNAME);
6420 0 : unsigned oldflags = bce->flags;
6421 0 : bce->flags &= ~TCF_IN_FOR_INIT;
6422 0 : if (!EmitTree(cx, bce, pn->pn_kid))
6423 0 : return false;
6424 0 : bce->flags |= oldflags & TCF_IN_FOR_INIT;
6425 0 : if (Emit1(cx, bce, op) < 0)
6426 0 : return false;
6427 : }
6428 54 : break;
6429 : #endif
6430 :
6431 : case PNK_THROW:
6432 : #if JS_HAS_XML_SUPPORT
6433 : case PNK_AT:
6434 : case PNK_DEFXMLNS:
6435 123413 : JS_ASSERT(pn->isArity(PN_UNARY));
6436 : /* FALL THROUGH */
6437 : #endif
6438 : case PNK_TYPEOF:
6439 : case PNK_VOID:
6440 : case PNK_NOT:
6441 : case PNK_BITNOT:
6442 : case PNK_POS:
6443 : case PNK_NEG:
6444 530286 : ok = EmitUnary(cx, bce, pn);
6445 530286 : break;
6446 :
6447 : case PNK_PREINCREMENT:
6448 : case PNK_PREDECREMENT:
6449 : case PNK_POSTINCREMENT:
6450 : case PNK_POSTDECREMENT:
6451 1732196 : ok = EmitIncOrDec(cx, bce, pn);
6452 1732196 : break;
6453 :
6454 : case PNK_DELETE:
6455 37438 : ok = EmitDelete(cx, bce, pn);
6456 37438 : break;
6457 :
6458 : #if JS_HAS_XML_SUPPORT
6459 : case PNK_FILTER:
6460 : {
6461 63 : JS_ASSERT(!bce->inStrictMode());
6462 :
6463 63 : if (!EmitTree(cx, bce, pn->pn_left))
6464 0 : return JS_FALSE;
6465 63 : ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0);
6466 63 : if (jmp < 0)
6467 0 : return JS_FALSE;
6468 63 : top = EmitLoopHead(cx, bce, pn->pn_right);
6469 63 : if (top < 0)
6470 0 : return JS_FALSE;
6471 63 : if (!EmitTree(cx, bce, pn->pn_right))
6472 0 : return JS_FALSE;
6473 63 : SetJumpOffsetAt(bce, jmp);
6474 63 : if (!EmitLoopEntry(cx, bce, NULL))
6475 0 : return false;
6476 63 : if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0)
6477 0 : return JS_FALSE;
6478 63 : break;
6479 : }
6480 : #endif
6481 :
6482 : case PNK_DOT:
6483 : /*
6484 : * Pop a stack operand, convert it to object, get a property named by
6485 : * this bytecode's immediate-indexed atom operand, and push its value
6486 : * (not a reference to it).
6487 : */
6488 5826638 : ok = EmitPropOp(cx, pn, pn->getOp(), bce, JS_FALSE);
6489 5826638 : break;
6490 :
6491 : #if JS_HAS_XML_SUPPORT
6492 : case PNK_DBLDOT:
6493 9 : JS_ASSERT(!bce->inStrictMode());
6494 : /* FALL THROUGH */
6495 : #endif
6496 : case PNK_LB:
6497 : /*
6498 : * Pop two operands, convert the left one to object and the right one
6499 : * to property name (atom or tagged int), get the named property, and
6500 : * push its value. Set the "obj" register to the result of ToObject
6501 : * on the left operand.
6502 : */
6503 272142 : ok = EmitElemOp(cx, pn, pn->getOp(), bce);
6504 272142 : break;
6505 :
6506 : case PNK_NEW:
6507 : case PNK_LP:
6508 3325217 : ok = EmitCallOrNew(cx, bce, pn, top);
6509 3325217 : break;
6510 :
6511 : case PNK_LEXICALSCOPE:
6512 366155 : ok = EmitLexicalScope(cx, bce, pn);
6513 366155 : break;
6514 :
6515 : #if JS_HAS_BLOCK_SCOPE
6516 : case PNK_LET:
6517 249755 : ok = pn->isArity(PN_BINARY)
6518 45060 : ? EmitLet(cx, bce, pn)
6519 294815 : : EmitVariables(cx, bce, pn, InitializeVars);
6520 249755 : break;
6521 : #endif /* JS_HAS_BLOCK_SCOPE */
6522 : #if JS_HAS_GENERATORS
6523 : case PNK_ARRAYPUSH: {
6524 : int slot;
6525 :
6526 : /*
6527 : * The array object's stack index is in bce->arrayCompDepth. See below
6528 : * under the array initialiser code generator for array comprehension
6529 : * special casing.
6530 : */
6531 12670 : if (!EmitTree(cx, bce, pn->pn_kid))
6532 0 : return JS_FALSE;
6533 12670 : slot = AdjustBlockSlot(cx, bce, bce->arrayCompDepth);
6534 12670 : if (slot < 0)
6535 0 : return JS_FALSE;
6536 12670 : EMIT_UINT16_IMM_OP(pn->getOp(), slot);
6537 12670 : break;
6538 : }
6539 : #endif
6540 :
6541 : case PNK_RB:
6542 : #if JS_HAS_GENERATORS
6543 : case PNK_ARRAYCOMP:
6544 : #endif
6545 223948 : ok = EmitArray(cx, bce, pn);
6546 223948 : break;
6547 :
6548 : case PNK_RC:
6549 216046 : ok = EmitObject(cx, bce, pn);
6550 216046 : break;
6551 :
6552 : case PNK_NAME:
6553 : /*
6554 : * Cope with a left-over function definition that was replaced by a use
6555 : * of a later function definition of the same name. See FunctionDef and
6556 : * MakeDefIntoUse in Parser.cpp.
6557 : */
6558 12057204 : if (pn->isOp(JSOP_NOP))
6559 0 : break;
6560 12057204 : if (!EmitNameOp(cx, bce, pn, JS_FALSE))
6561 0 : return JS_FALSE;
6562 12057204 : break;
6563 :
6564 : #if JS_HAS_XML_SUPPORT
6565 : case PNK_XMLATTR:
6566 : case PNK_XMLSPACE:
6567 : case PNK_XMLTEXT:
6568 : case PNK_XMLCDATA:
6569 : case PNK_XMLCOMMENT:
6570 335 : JS_ASSERT(!bce->inStrictMode());
6571 : /* FALL THROUGH */
6572 : #endif
6573 : case PNK_STRING:
6574 6886168 : ok = EmitAtomOp(cx, pn, pn->getOp(), bce);
6575 6886168 : break;
6576 :
6577 : case PNK_NUMBER:
6578 5871826 : ok = EmitNumberOp(cx, pn->pn_dval, bce);
6579 5871826 : break;
6580 :
6581 : case PNK_REGEXP:
6582 33976 : JS_ASSERT(pn->isOp(JSOP_REGEXP));
6583 33976 : ok = EmitRegExp(cx, bce->regexpList.index(pn->pn_objbox), bce);
6584 33976 : break;
6585 :
6586 : #if JS_HAS_XML_SUPPORT
6587 : case PNK_ANYNAME:
6588 : #endif
6589 : case PNK_TRUE:
6590 : case PNK_FALSE:
6591 : case PNK_THIS:
6592 : case PNK_NULL:
6593 2382889 : if (Emit1(cx, bce, pn->getOp()) < 0)
6594 0 : return JS_FALSE;
6595 2382889 : break;
6596 :
6597 : case PNK_DEBUGGER:
6598 4397 : if (Emit1(cx, bce, JSOP_DEBUGGER) < 0)
6599 0 : return JS_FALSE;
6600 4397 : break;
6601 :
6602 : #if JS_HAS_XML_SUPPORT
6603 : case PNK_XMLELEM:
6604 : case PNK_XMLLIST:
6605 308 : JS_ASSERT(!bce->inStrictMode());
6606 308 : JS_ASSERT(pn->isKind(PNK_XMLLIST) || pn->pn_count != 0);
6607 :
6608 308 : switch (pn->pn_head ? pn->pn_head->getKind() : PNK_XMLLIST) {
6609 : case PNK_XMLETAGO:
6610 0 : JS_ASSERT(0);
6611 : /* FALL THROUGH */
6612 : case PNK_XMLPTAGC:
6613 : case PNK_XMLSTAGO:
6614 9 : break;
6615 : default:
6616 299 : if (Emit1(cx, bce, JSOP_STARTXML) < 0)
6617 0 : return JS_FALSE;
6618 : }
6619 :
6620 634 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6621 326 : if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0)
6622 0 : return JS_FALSE;
6623 326 : if (!EmitTree(cx, bce, pn2))
6624 0 : return JS_FALSE;
6625 326 : if (pn2 != pn->pn_head && Emit1(cx, bce, JSOP_ADD) < 0)
6626 0 : return JS_FALSE;
6627 : }
6628 :
6629 308 : if (pn->pn_xflags & PNX_XMLROOT) {
6630 308 : if (pn->pn_count == 0) {
6631 0 : JS_ASSERT(pn->isKind(PNK_XMLLIST));
6632 0 : JSAtom *atom = cx->runtime->atomState.emptyAtom;
6633 : jsatomid index;
6634 0 : if (!bce->makeAtomIndex(atom, &index))
6635 0 : return JS_FALSE;
6636 0 : if (!EmitIndex32(cx, JSOP_STRING, index, bce))
6637 0 : return false;
6638 : }
6639 308 : if (Emit1(cx, bce, pn->getOp()) < 0)
6640 0 : return JS_FALSE;
6641 : }
6642 : #ifdef DEBUG
6643 : else
6644 0 : JS_ASSERT(pn->pn_count != 0);
6645 : #endif
6646 308 : break;
6647 :
6648 : case PNK_XMLPTAGC:
6649 : case PNK_XMLSTAGO:
6650 : case PNK_XMLETAGO:
6651 36 : if (!EmitXMLTag(cx, bce, pn))
6652 0 : return false;
6653 36 : break;
6654 :
6655 : case PNK_XMLNAME:
6656 36 : JS_ASSERT(!bce->inStrictMode());
6657 :
6658 36 : if (pn->isArity(PN_LIST)) {
6659 0 : JS_ASSERT(pn->pn_count != 0);
6660 0 : for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
6661 0 : if (pn2->isKind(PNK_XMLCURLYEXPR) && Emit1(cx, bce, JSOP_STARTXMLEXPR) < 0)
6662 0 : return JS_FALSE;
6663 0 : if (!EmitTree(cx, bce, pn2))
6664 0 : return JS_FALSE;
6665 0 : if (pn2 != pn->pn_head && Emit1(cx, bce, JSOP_ADD) < 0)
6666 0 : return JS_FALSE;
6667 : }
6668 : } else {
6669 36 : JS_ASSERT(pn->isArity(PN_NULLARY));
6670 36 : ok = pn->isOp(JSOP_OBJECT)
6671 0 : ? EmitObjectOp(cx, pn->pn_objbox, pn->getOp(), bce)
6672 36 : : EmitAtomOp(cx, pn, pn->getOp(), bce);
6673 : }
6674 36 : break;
6675 :
6676 : case PNK_XMLPI:
6677 0 : if (!EmitXMLProcessingInstruction(cx, bce, pn->asXMLProcessingInstruction()))
6678 0 : return false;
6679 0 : break;
6680 : #endif /* JS_HAS_XML_SUPPORT */
6681 :
6682 : default:
6683 0 : JS_ASSERT(0);
6684 : }
6685 :
6686 : /* bce->emitLevel == 1 means we're last on the stack, so finish up. */
6687 59153270 : if (ok && bce->emitLevel == 1) {
6688 1875805 : if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.end.lineno))
6689 0 : return JS_FALSE;
6690 : }
6691 :
6692 59153270 : return ok;
6693 : }
6694 :
6695 : static int
6696 69363990 : AllocSrcNote(JSContext *cx, BytecodeEmitter *bce)
6697 : {
6698 69363990 : jssrcnote *notes = bce->notes();
6699 : jssrcnote *newnotes;
6700 69363990 : unsigned index = bce->noteCount();
6701 69363990 : unsigned max = bce->noteLimit();
6702 :
6703 69363990 : if (index == max) {
6704 : size_t newlength;
6705 1044544 : if (!notes) {
6706 1034454 : JS_ASSERT(!index && !max);
6707 1034454 : newlength = SRCNOTE_CHUNK_LENGTH;
6708 1034454 : newnotes = (jssrcnote *) cx->malloc_(SRCNOTE_SIZE(newlength));
6709 : } else {
6710 10090 : JS_ASSERT(index <= max);
6711 10090 : newlength = max * 2;
6712 10090 : newnotes = (jssrcnote *) cx->realloc_(notes, SRCNOTE_SIZE(newlength));
6713 : }
6714 1044544 : if (!newnotes) {
6715 0 : js_ReportOutOfMemory(cx);
6716 0 : return -1;
6717 : }
6718 1044544 : bce->current->notes = newnotes;
6719 1044544 : bce->current->noteLimit = newlength;
6720 : }
6721 :
6722 69363990 : bce->current->noteCount = index + 1;
6723 69363990 : return (int)index;
6724 : }
6725 :
6726 : int
6727 58002893 : frontend::NewSrcNote(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type)
6728 : {
6729 : int index, n;
6730 : jssrcnote *sn;
6731 : ptrdiff_t offset, delta, xdelta;
6732 :
6733 : /*
6734 : * Claim a note slot in bce->notes() by growing it if necessary and then
6735 : * incrementing bce->noteCount().
6736 : */
6737 58002893 : index = AllocSrcNote(cx, bce);
6738 58002893 : if (index < 0)
6739 0 : return -1;
6740 58002893 : sn = &bce->notes()[index];
6741 :
6742 : /*
6743 : * Compute delta from the last annotated bytecode's offset. If it's too
6744 : * big to fit in sn, allocate one or more xdelta notes and reset sn.
6745 : */
6746 58002893 : offset = bce->offset();
6747 58002893 : delta = offset - bce->lastNoteOffset();
6748 58002893 : bce->current->lastNoteOffset = offset;
6749 58002893 : if (delta >= SN_DELTA_LIMIT) {
6750 11361097 : do {
6751 11361097 : xdelta = JS_MIN(delta, SN_XDELTA_MASK);
6752 11361097 : SN_MAKE_XDELTA(sn, xdelta);
6753 11361097 : delta -= xdelta;
6754 11361097 : index = AllocSrcNote(cx, bce);
6755 11361097 : if (index < 0)
6756 0 : return -1;
6757 11361097 : sn = &bce->notes()[index];
6758 : } while (delta >= SN_DELTA_LIMIT);
6759 : }
6760 :
6761 : /*
6762 : * Initialize type and delta, then allocate the minimum number of notes
6763 : * needed for type's arity. Usually, we won't need more, but if an offset
6764 : * does take two bytes, SetSrcNoteOffset will grow bce->notes().
6765 : */
6766 58002893 : SN_MAKE_NOTE(sn, type, delta);
6767 79968402 : for (n = (int)js_SrcNoteSpec[type].arity; n > 0; n--) {
6768 21965509 : if (NewSrcNote(cx, bce, SRC_NULL) < 0)
6769 0 : return -1;
6770 : }
6771 58002893 : return index;
6772 : }
6773 :
6774 : int
6775 21049064 : frontend::NewSrcNote2(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset)
6776 : {
6777 : int index;
6778 :
6779 21049064 : index = NewSrcNote(cx, bce, type);
6780 21049064 : if (index >= 0) {
6781 21049064 : if (!SetSrcNoteOffset(cx, bce, index, 0, offset))
6782 0 : return -1;
6783 : }
6784 21049064 : return index;
6785 : }
6786 :
6787 : int
6788 17196 : frontend::NewSrcNote3(JSContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1,
6789 : ptrdiff_t offset2)
6790 : {
6791 : int index;
6792 :
6793 17196 : index = NewSrcNote(cx, bce, type);
6794 17196 : if (index >= 0) {
6795 17196 : if (!SetSrcNoteOffset(cx, bce, index, 0, offset1))
6796 0 : return -1;
6797 17196 : if (!SetSrcNoteOffset(cx, bce, index, 1, offset2))
6798 0 : return -1;
6799 : }
6800 17196 : return index;
6801 : }
6802 :
6803 : static JSBool
6804 35 : GrowSrcNotes(JSContext *cx, BytecodeEmitter *bce)
6805 : {
6806 35 : size_t newlength = bce->noteLimit() * 2;
6807 35 : jssrcnote *newnotes = (jssrcnote *) cx->realloc_(bce->notes(), newlength);
6808 35 : if (!newnotes) {
6809 0 : js_ReportOutOfMemory(cx);
6810 0 : return JS_FALSE;
6811 : }
6812 35 : bce->current->notes = newnotes;
6813 35 : bce->current->noteLimit = newlength;
6814 35 : return JS_TRUE;
6815 : }
6816 :
6817 : jssrcnote *
6818 60933 : frontend::AddToSrcNoteDelta(JSContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta)
6819 : {
6820 : ptrdiff_t base, limit, newdelta, diff;
6821 : int index;
6822 :
6823 : /*
6824 : * Called only from OptimizeSpanDeps and FinishTakingSrcNotes to add to
6825 : * main script note deltas, and only by a small positive amount.
6826 : */
6827 60933 : JS_ASSERT(bce->current == &bce->main);
6828 60933 : JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
6829 :
6830 60933 : base = SN_DELTA(sn);
6831 60933 : limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
6832 60933 : newdelta = base + delta;
6833 60933 : if (newdelta < limit) {
6834 33904 : SN_SET_DELTA(sn, newdelta);
6835 : } else {
6836 27029 : index = sn - bce->main.notes;
6837 27029 : if (bce->main.noteCount == bce->main.noteLimit) {
6838 0 : if (!GrowSrcNotes(cx, bce))
6839 0 : return NULL;
6840 0 : sn = bce->main.notes + index;
6841 : }
6842 27029 : diff = bce->main.noteCount - index;
6843 27029 : bce->main.noteCount++;
6844 27029 : memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
6845 27029 : SN_MAKE_XDELTA(sn, delta);
6846 27029 : sn++;
6847 : }
6848 60933 : return sn;
6849 : }
6850 :
6851 : static JSBool
6852 21997593 : SetSrcNoteOffset(JSContext *cx, BytecodeEmitter *bce, unsigned index, unsigned which, ptrdiff_t offset)
6853 : {
6854 : jssrcnote *sn;
6855 : ptrdiff_t diff;
6856 :
6857 21997593 : if (size_t(offset) > SN_MAX_OFFSET) {
6858 0 : ReportStatementTooLarge(cx, bce);
6859 0 : return JS_FALSE;
6860 : }
6861 :
6862 : /* Find the offset numbered which (i.e., skip exactly which offsets). */
6863 21997593 : sn = &bce->notes()[index];
6864 21997593 : JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
6865 21997593 : JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
6866 22298400 : for (sn++; which; sn++, which--) {
6867 300807 : if (*sn & SN_3BYTE_OFFSET_FLAG)
6868 51641 : sn += 2;
6869 : }
6870 :
6871 : /*
6872 : * See if the new offset requires three bytes either by being too big or if
6873 : * the offset has already been inflated (in which case, we need to stay big
6874 : * to not break the srcnote encoding if this isn't the last srcnote).
6875 : */
6876 21997593 : if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK || (*sn & SN_3BYTE_OFFSET_FLAG)) {
6877 : /* Maybe this offset was already set to a three-byte value. */
6878 1660927 : if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
6879 : /* Losing, need to insert another two bytes for this offset. */
6880 1656769 : index = sn - bce->notes();
6881 :
6882 : /*
6883 : * Test to see if the source note array must grow to accommodate
6884 : * either the first or second byte of additional storage required
6885 : * by this 3-byte offset.
6886 : */
6887 1656769 : if (bce->noteCount() + 1 >= bce->noteLimit()) {
6888 35 : if (!GrowSrcNotes(cx, bce))
6889 0 : return JS_FALSE;
6890 35 : sn = bce->notes() + index;
6891 : }
6892 1656769 : bce->current->noteCount += 2;
6893 :
6894 1656769 : diff = bce->noteCount() - (index + 3);
6895 1656769 : JS_ASSERT(diff >= 0);
6896 1656769 : if (diff > 0)
6897 190344 : memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
6898 : }
6899 1660927 : *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
6900 1660927 : *sn++ = (jssrcnote)(offset >> 8);
6901 : }
6902 21997593 : *sn = (jssrcnote)offset;
6903 21997593 : return JS_TRUE;
6904 : }
6905 :
6906 : #ifdef DEBUG_notme
6907 : #define DEBUG_srcnotesize
6908 : #endif
6909 :
6910 : #ifdef DEBUG_srcnotesize
6911 : #define NBINS 10
6912 : static uint32_t hist[NBINS];
6913 :
6914 : static void
6915 : DumpSrcNoteSizeHist()
6916 : {
6917 : static FILE *fp;
6918 : int i, n;
6919 :
6920 : if (!fp) {
6921 : fp = fopen("/tmp/srcnotes.hist", "w");
6922 : if (!fp)
6923 : return;
6924 : setvbuf(fp, NULL, _IONBF, 0);
6925 : }
6926 : fprintf(fp, "SrcNote size histogram:\n");
6927 : for (i = 0; i < NBINS; i++) {
6928 : fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]);
6929 : for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n)
6930 : fputc('*', fp);
6931 : fputc('\n', fp);
6932 : }
6933 : fputc('\n', fp);
6934 : }
6935 : #endif
6936 :
6937 : /*
6938 : * Fill in the storage at notes with prolog and main srcnotes; the space at
6939 : * notes was allocated using the BytecodeEmitter::countFinalSourceNotes()
6940 : * method from BytecodeEmitter.h. SO DON'T CHANGE THIS FUNCTION WITHOUT AT
6941 : * LEAST CHECKING WHETHER BytecodeEmitter::countFinalSourceNotes() NEEDS
6942 : * CORRESPONDING CHANGES!
6943 : */
6944 : JSBool
6945 1106879 : frontend::FinishTakingSrcNotes(JSContext *cx, BytecodeEmitter *bce, jssrcnote *notes)
6946 : {
6947 : unsigned prologCount, mainCount, totalCount;
6948 : ptrdiff_t offset, delta;
6949 : jssrcnote *sn;
6950 :
6951 1106879 : JS_ASSERT(bce->current == &bce->main);
6952 :
6953 1106879 : prologCount = bce->prolog.noteCount;
6954 1106879 : if (prologCount && bce->prolog.currentLine != bce->firstLine) {
6955 17366 : bce->switchToProlog();
6956 17366 : if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)bce->firstLine) < 0)
6957 0 : return false;
6958 17366 : prologCount = bce->prolog.noteCount;
6959 17366 : bce->switchToMain();
6960 : } else {
6961 : /*
6962 : * Either no prolog srcnotes, or no line number change over prolog.
6963 : * We don't need a SRC_SETLINE, but we may need to adjust the offset
6964 : * of the first main note, by adding to its delta and possibly even
6965 : * prepending SRC_XDELTA notes to it to account for prolog bytecodes
6966 : * that came at and after the last annotated bytecode.
6967 : */
6968 1089513 : offset = bce->prologOffset() - bce->prolog.lastNoteOffset;
6969 1089513 : JS_ASSERT(offset >= 0);
6970 1089513 : if (offset > 0 && bce->main.noteCount != 0) {
6971 : /* NB: Use as much of the first main note's delta as we can. */
6972 33904 : sn = bce->main.notes;
6973 : delta = SN_IS_XDELTA(sn)
6974 : ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
6975 33904 : : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
6976 33904 : if (offset < delta)
6977 6857 : delta = offset;
6978 27029 : for (;;) {
6979 60933 : if (!AddToSrcNoteDelta(cx, bce, sn, delta))
6980 0 : return false;
6981 60933 : offset -= delta;
6982 60933 : if (offset == 0)
6983 : break;
6984 27029 : delta = JS_MIN(offset, SN_XDELTA_MASK);
6985 27029 : sn = bce->main.notes;
6986 : }
6987 : }
6988 : }
6989 :
6990 1106879 : mainCount = bce->main.noteCount;
6991 1106879 : totalCount = prologCount + mainCount;
6992 1106879 : if (prologCount)
6993 17366 : PodCopy(notes, bce->prolog.notes, prologCount);
6994 1106879 : PodCopy(notes + prologCount, bce->main.notes, mainCount);
6995 1106879 : SN_MAKE_TERMINATOR(¬es[totalCount]);
6996 :
6997 1106879 : return true;
6998 : }
6999 :
7000 : static JSBool
7001 301250 : NewTryNote(JSContext *cx, BytecodeEmitter *bce, JSTryNoteKind kind, unsigned stackDepth, size_t start,
7002 : size_t end)
7003 : {
7004 301250 : JS_ASSERT((unsigned)(uint16_t)stackDepth == stackDepth);
7005 301250 : JS_ASSERT(start <= end);
7006 : JS_ASSERT((size_t)(uint32_t)start == start);
7007 : JS_ASSERT((size_t)(uint32_t)end == end);
7008 :
7009 301250 : TryNode *tryNode = cx->tempLifoAlloc().new_<TryNode>();
7010 301250 : if (!tryNode) {
7011 0 : js_ReportOutOfMemory(cx);
7012 0 : return JS_FALSE;
7013 : }
7014 :
7015 301250 : tryNode->note.kind = kind;
7016 301250 : tryNode->note.stackDepth = (uint16_t)stackDepth;
7017 301250 : tryNode->note.start = (uint32_t)start;
7018 301250 : tryNode->note.length = (uint32_t)(end - start);
7019 301250 : tryNode->prev = bce->lastTryNode;
7020 301250 : bce->lastTryNode = tryNode;
7021 301250 : bce->ntrynotes++;
7022 301250 : return JS_TRUE;
7023 : }
7024 :
7025 : void
7026 141723 : frontend::FinishTakingTryNotes(BytecodeEmitter *bce, JSTryNoteArray *array)
7027 : {
7028 : TryNode *tryNode;
7029 : JSTryNote *tn;
7030 :
7031 141723 : JS_ASSERT(array->length > 0 && array->length == bce->ntrynotes);
7032 141723 : tn = array->vector + array->length;
7033 141723 : tryNode = bce->lastTryNode;
7034 301241 : do {
7035 301241 : *--tn = tryNode->note;
7036 : } while ((tryNode = tryNode->prev) != NULL);
7037 141723 : JS_ASSERT(tn == array->vector);
7038 141723 : }
7039 :
7040 : /*
7041 : * Find the index of the given object for code generator.
7042 : *
7043 : * Since the emitter refers to each parsed object only once, for the index we
7044 : * use the number of already indexes objects. We also add the object to a list
7045 : * to convert the list to a fixed-size array when we complete code generation,
7046 : * see js::CGObjectList::finish below.
7047 : *
7048 : * Most of the objects go to BytecodeEmitter::objectList but for regexp we use
7049 : * a separated BytecodeEmitter::regexpList. In this way the emitted index can
7050 : * be directly used to store and fetch a reference to a cloned RegExp object
7051 : * that shares the same JSRegExp private data created for the object literal in
7052 : * objbox. We need a cloned object to hold lastIndex and other direct
7053 : * properties that should not be shared among threads sharing a precompiled
7054 : * function or script.
7055 : *
7056 : * If the code being compiled is function code, allocate a reserved slot in
7057 : * the cloned function object that shares its precompiled script with other
7058 : * cloned function objects and with the compiler-created clone-parent. There
7059 : * are nregexps = script->regexps()->length such reserved slots in each
7060 : * function object cloned from fun->object. NB: during compilation, a funobj
7061 : * slots element must never be allocated, because JSObject::allocSlot could
7062 : * hand out one of the slots that should be given to a regexp clone.
7063 : *
7064 : * If the code being compiled is global code, the cloned regexp are stored in
7065 : * fp->vars slot and to protect regexp slots from GC we set fp->nvars to
7066 : * nregexps.
7067 : *
7068 : * The slots initially contain undefined or null. We populate them lazily when
7069 : * JSOP_REGEXP is executed for the first time.
7070 : *
7071 : * Why clone regexp objects? ECMA specifies that when a regular expression
7072 : * literal is scanned, a RegExp object is created. In the spec, compilation
7073 : * and execution happen indivisibly, but in this implementation and many of
7074 : * its embeddings, code is precompiled early and re-executed in multiple
7075 : * threads, or using multiple global objects, or both, for efficiency.
7076 : *
7077 : * In such cases, naively following ECMA leads to wrongful sharing of RegExp
7078 : * objects, which makes for collisions on the lastIndex property (especially
7079 : * for global regexps) and on any ad-hoc properties. Also, __proto__ refers to
7080 : * the pre-compilation prototype, a pigeon-hole problem for instanceof tests.
7081 : */
7082 : unsigned
7083 1471365 : CGObjectList::index(ObjectBox *objbox)
7084 : {
7085 1471365 : JS_ASSERT(!objbox->emitLink);
7086 1471365 : objbox->emitLink = lastbox;
7087 1471365 : lastbox = objbox;
7088 1471365 : return length++;
7089 : }
7090 :
7091 : void
7092 402000 : CGObjectList::finish(JSObjectArray *array)
7093 : {
7094 402000 : JS_ASSERT(length <= INDEX_LIMIT);
7095 402000 : JS_ASSERT(length == array->length);
7096 :
7097 402000 : js::HeapPtrObject *cursor = array->vector + array->length;
7098 402000 : ObjectBox *objbox = lastbox;
7099 1492272 : do {
7100 1492272 : --cursor;
7101 1492272 : JS_ASSERT(!*cursor);
7102 1492272 : *cursor = objbox->object;
7103 : } while ((objbox = objbox->emitLink) != NULL);
7104 402000 : JS_ASSERT(cursor == array->vector);
7105 402000 : }
7106 :
7107 : void
7108 12767 : GCConstList::finish(JSConstArray *array)
7109 : {
7110 12767 : JS_ASSERT(array->length == list.length());
7111 12767 : Value *src = list.begin(), *srcend = list.end();
7112 12767 : HeapValue *dst = array->vector;
7113 67975 : for (; src != srcend; ++src, ++dst)
7114 55208 : *dst = *src;
7115 12767 : }
7116 :
7117 : /*
7118 : * We should try to get rid of offsetBias (always 0 or 1, where 1 is
7119 : * JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL.
7120 : */
7121 : JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
7122 : {"null", 0},
7123 : {"if", 0},
7124 : {"if-else", 2},
7125 : {"for", 3},
7126 : {"while", 1},
7127 : {"continue", 0},
7128 : {"decl", 1},
7129 : {"pcdelta", 1},
7130 : {"assignop", 0},
7131 : {"cond", 1},
7132 : {"brace", 1},
7133 : {"hidden", 0},
7134 : {"pcbase", 1},
7135 : {"label", 1},
7136 : {"labelbrace", 1},
7137 : {"endbrace", 0},
7138 : {"break2label", 1},
7139 : {"cont2label", 1},
7140 : {"switch", 2},
7141 : {"funcdef", 1},
7142 : {"catch", 1},
7143 : {"unused", -1},
7144 : {"newline", 0},
7145 : {"setline", 1},
7146 : {"xdelta", 0},
7147 : };
7148 :
7149 : JS_FRIEND_API(unsigned)
7150 131811715 : js_SrcNoteLength(jssrcnote *sn)
7151 : {
7152 : unsigned arity;
7153 : jssrcnote *base;
7154 :
7155 131811715 : arity = (int)js_SrcNoteSpec[SN_TYPE(sn)].arity;
7156 267434274 : for (base = sn++; arity; sn++, arity--) {
7157 135622559 : if (*sn & SN_3BYTE_OFFSET_FLAG)
7158 6070059 : sn += 2;
7159 : }
7160 131811715 : return sn - base;
7161 : }
7162 :
7163 : JS_FRIEND_API(ptrdiff_t)
7164 3549578 : js_GetSrcNoteOffset(jssrcnote *sn, unsigned which)
7165 : {
7166 : /* Find the offset numbered which (i.e., skip exactly which offsets). */
7167 3549578 : JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
7168 3549578 : JS_ASSERT((int) which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
7169 3553781 : for (sn++; which; sn++, which--) {
7170 4203 : if (*sn & SN_3BYTE_OFFSET_FLAG)
7171 9 : sn += 2;
7172 : }
7173 3549578 : if (*sn & SN_3BYTE_OFFSET_FLAG) {
7174 1717336 : return (ptrdiff_t)(((uint32_t)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
7175 1717336 : | (sn[1] << 8)
7176 3434672 : | sn[2]);
7177 : }
7178 1832242 : return (ptrdiff_t)*sn;
7179 : }
|