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