1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set sw=4 ts=8 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 descriptors, disassemblers, and decompilers.
43 : */
44 : #ifdef HAVE_MEMORY_H
45 : #include <memory.h>
46 : #endif
47 : #include <stdarg.h>
48 : #include <stdio.h>
49 : #include <stdlib.h>
50 : #include <string.h>
51 :
52 : #include "mozilla/Util.h"
53 :
54 : #include "jstypes.h"
55 : #include "jsutil.h"
56 : #include "jsprf.h"
57 : #include "jsapi.h"
58 : #include "jsarray.h"
59 : #include "jsatom.h"
60 : #include "jscntxt.h"
61 : #include "jsversion.h"
62 : #include "jsfun.h"
63 : #include "jsiter.h"
64 : #include "jsnum.h"
65 : #include "jsobj.h"
66 : #include "jsopcode.h"
67 : #include "jsscope.h"
68 : #include "jsscript.h"
69 : #include "jsstr.h"
70 :
71 : #include "ds/Sort.h"
72 :
73 : #include "frontend/BytecodeEmitter.h"
74 : #include "frontend/TokenStream.h"
75 : #include "vm/Debugger.h"
76 :
77 : #include "jscntxtinlines.h"
78 : #include "jsobjinlines.h"
79 : #include "jsopcodeinlines.h"
80 :
81 : #include "jsautooplen.h"
82 :
83 : #include "vm/RegExpObject-inl.h"
84 : #include "vm/StringBuffer-inl.h"
85 :
86 : using namespace mozilla;
87 : using namespace js;
88 : using namespace js::gc;
89 :
90 : /*
91 : * Index limit must stay within 32 bits.
92 : */
93 : JS_STATIC_ASSERT(sizeof(uint32_t) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1);
94 :
95 : /* Verify JSOP_XXX_LENGTH constant definitions. */
96 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
97 : JS_STATIC_ASSERT(op##_LENGTH == length);
98 : #include "jsopcode.tbl"
99 : #undef OPDEF
100 :
101 : static const char js_incop_strs[][3] = {"++", "--"};
102 : static const char js_for_each_str[] = "for each";
103 :
104 : const JSCodeSpec js_CodeSpec[] = {
105 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
106 : {length,nuses,ndefs,prec,format},
107 : #include "jsopcode.tbl"
108 : #undef OPDEF
109 : };
110 :
111 : unsigned js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
112 :
113 : /*
114 : * Each element of the array is either a source literal associated with JS
115 : * bytecode or null.
116 : */
117 : static const char *CodeToken[] = {
118 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
119 : token,
120 : #include "jsopcode.tbl"
121 : #undef OPDEF
122 : };
123 :
124 : /*
125 : * Array of JS bytecode names used by PC count JSON, DEBUG-only js_Disassemble
126 : * and JIT debug spew.
127 : */
128 : const char *js_CodeName[] = {
129 : #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
130 : name,
131 : #include "jsopcode.tbl"
132 : #undef OPDEF
133 : };
134 :
135 : /************************************************************************/
136 :
137 : #define COUNTS_LEN 16
138 :
139 : typedef Vector<char, 8> DupBuffer;
140 :
141 : static bool
142 6273 : Dup(const char *chars, DupBuffer *cb)
143 : {
144 6273 : return cb->append(chars, strlen(chars) + 1);
145 : }
146 :
147 : size_t
148 12172 : js_GetVariableBytecodeLength(jsbytecode *pc)
149 : {
150 : unsigned ncases;
151 : int32_t low, high;
152 :
153 12172 : JSOp op = JSOp(*pc);
154 12172 : JS_ASSERT(js_CodeSpec[op].length == -1);
155 12172 : switch (op) {
156 : case JSOP_TABLESWITCH:
157 : /* Structure: default-jump case-low case-high case1-jump ... */
158 3172 : pc += JUMP_OFFSET_LEN;
159 3172 : low = GET_JUMP_OFFSET(pc);
160 3172 : pc += JUMP_OFFSET_LEN;
161 3172 : high = GET_JUMP_OFFSET(pc);
162 3172 : ncases = (unsigned)(high - low + 1);
163 3172 : return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
164 :
165 : default:
166 : /* Structure: default-jump case-count (case1-value case1-jump) ... */
167 9000 : JS_ASSERT(op == JSOP_LOOKUPSWITCH);
168 9000 : pc += JUMP_OFFSET_LEN;
169 9000 : ncases = GET_UINT16(pc);
170 9000 : return 1 + JUMP_OFFSET_LEN + UINT16_LEN + ncases * (UINT32_INDEX_LEN + JUMP_OFFSET_LEN);
171 : }
172 : }
173 :
174 : static uint32_t
175 597351 : NumBlockSlots(JSScript *script, jsbytecode *pc)
176 : {
177 597351 : JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_ENTERLET0 || *pc == JSOP_ENTERLET1);
178 : JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH);
179 : JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH);
180 :
181 597351 : return script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock().slotCount();
182 : }
183 :
184 : unsigned
185 147260851 : js::StackUses(JSScript *script, jsbytecode *pc)
186 : {
187 147260851 : JSOp op = (JSOp) *pc;
188 147260851 : const JSCodeSpec &cs = js_CodeSpec[op];
189 147260851 : if (cs.nuses >= 0)
190 137995845 : return cs.nuses;
191 :
192 9265006 : JS_ASSERT(js_CodeSpec[op].nuses == -1);
193 9265006 : switch (op) {
194 : case JSOP_POPN:
195 69156 : return GET_UINT16(pc);
196 : case JSOP_LEAVEBLOCK:
197 990299 : return GET_UINT16(pc);
198 : case JSOP_LEAVEBLOCKEXPR:
199 4559 : return GET_UINT16(pc) + 1;
200 : case JSOP_ENTERLET0:
201 36213 : return NumBlockSlots(script, pc);
202 : case JSOP_ENTERLET1:
203 32428 : return NumBlockSlots(script, pc) + 1;
204 : default:
205 : /* stack: fun, this, [argc arguments] */
206 0 : JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
207 8132351 : op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
208 8132351 : return 2 + GET_ARGC(pc);
209 : }
210 : }
211 :
212 : unsigned
213 297188418 : js::StackDefs(JSScript *script, jsbytecode *pc)
214 : {
215 297188418 : JSOp op = (JSOp) *pc;
216 297188418 : const JSCodeSpec &cs = js_CodeSpec[op];
217 297188418 : if (cs.ndefs >= 0)
218 296659708 : return cs.ndefs;
219 :
220 528710 : uint32_t n = NumBlockSlots(script, pc);
221 528710 : return op == JSOP_ENTERLET1 ? n + 1 : n;
222 : }
223 :
224 : static const char * countBaseNames[] = {
225 : "interp",
226 : "mjit",
227 : "mjit_calls",
228 : "mjit_code",
229 : "mjit_pics"
230 : };
231 :
232 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) == OpcodeCounts::BASE_COUNT);
233 :
234 : static const char * countAccessNames[] = {
235 : "infer_mono",
236 : "infer_di",
237 : "infer_poly",
238 : "infer_barrier",
239 : "infer_nobarrier",
240 : "observe_undefined",
241 : "observe_null",
242 : "observe_boolean",
243 : "observe_int32",
244 : "observe_double",
245 : "observe_string",
246 : "observe_object"
247 : };
248 :
249 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
250 : JS_ARRAY_LENGTH(countAccessNames) == OpcodeCounts::ACCESS_COUNT);
251 :
252 : static const char * countElementNames[] = {
253 : "id_int",
254 : "id_double",
255 : "id_other",
256 : "id_unknown",
257 : "elem_typed",
258 : "elem_packed",
259 : "elem_dense",
260 : "elem_other"
261 : };
262 :
263 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
264 : JS_ARRAY_LENGTH(countAccessNames) +
265 : JS_ARRAY_LENGTH(countElementNames) == OpcodeCounts::ELEM_COUNT);
266 :
267 : static const char * countPropertyNames[] = {
268 : "prop_static",
269 : "prop_definite",
270 : "prop_other"
271 : };
272 :
273 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
274 : JS_ARRAY_LENGTH(countAccessNames) +
275 : JS_ARRAY_LENGTH(countPropertyNames) == OpcodeCounts::PROP_COUNT);
276 :
277 : static const char * countArithNames[] = {
278 : "arith_int",
279 : "arith_double",
280 : "arith_other",
281 : "arith_unknown",
282 : };
283 :
284 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(countBaseNames) +
285 : JS_ARRAY_LENGTH(countArithNames) == OpcodeCounts::ARITH_COUNT);
286 :
287 : /* static */ const char *
288 0 : OpcodeCounts::countName(JSOp op, size_t which)
289 : {
290 0 : JS_ASSERT(which < numCounts(op));
291 :
292 0 : if (which < BASE_COUNT)
293 0 : return countBaseNames[which];
294 :
295 0 : if (accessOp(op)) {
296 0 : if (which < ACCESS_COUNT)
297 0 : return countAccessNames[which - BASE_COUNT];
298 0 : if (elementOp(op))
299 0 : return countElementNames[which - ACCESS_COUNT];
300 0 : if (propertyOp(op))
301 0 : return countPropertyNames[which - ACCESS_COUNT];
302 0 : JS_NOT_REACHED("bad op");
303 : return NULL;
304 : }
305 :
306 0 : if (arithOp(op))
307 0 : return countArithNames[which - BASE_COUNT];
308 :
309 0 : JS_NOT_REACHED("bad op");
310 : return NULL;
311 : }
312 :
313 : #ifdef DEBUG
314 :
315 : JS_FRIEND_API(void)
316 0 : js_DumpPCCounts(JSContext *cx, JSScript *script, js::Sprinter *sp)
317 : {
318 0 : JS_ASSERT(script->pcCounters);
319 :
320 0 : jsbytecode *pc = script->code;
321 0 : while (pc < script->code + script->length) {
322 0 : JSOp op = JSOp(*pc);
323 :
324 0 : int len = js_CodeSpec[op].length;
325 0 : jsbytecode *next = (len != -1) ? pc + len : pc + js_GetVariableBytecodeLength(pc);
326 :
327 0 : if (!js_Disassemble1(cx, script, pc, pc - script->code, true, sp))
328 0 : return;
329 :
330 0 : size_t total = OpcodeCounts::numCounts(op);
331 0 : double *raw = script->getCounts(pc).rawCounts();
332 :
333 0 : Sprint(sp, " {");
334 0 : bool printed = false;
335 0 : for (size_t i = 0; i < total; i++) {
336 0 : double val = raw[i];
337 0 : if (val) {
338 0 : if (printed)
339 0 : Sprint(sp, ", ");
340 0 : Sprint(sp, "\"%s\": %.0f", OpcodeCounts::countName(op, i), val);
341 0 : printed = true;
342 : }
343 : }
344 0 : Sprint(sp, "}\n");
345 :
346 0 : pc = next;
347 : }
348 : }
349 :
350 : /*
351 : * If pc != NULL, include a prefix indicating whether the PC is at the current line.
352 : * If counts != NULL, include a counter of the number of times each op was executed.
353 : */
354 : JS_FRIEND_API(JSBool)
355 9 : js_DisassembleAtPC(JSContext *cx, JSScript *script, JSBool lines, jsbytecode *pc, Sprinter *sp)
356 : {
357 : jsbytecode *next, *end;
358 : unsigned len;
359 :
360 9 : sp->put("loc ");
361 9 : if (lines)
362 0 : sp->put("line");
363 9 : sp->put(" op\n");
364 9 : sp->put("----- ");
365 9 : if (lines)
366 0 : sp->put("----");
367 9 : sp->put(" --\n");
368 :
369 9 : next = script->code;
370 9 : end = next + script->length;
371 147438 : while (next < end) {
372 147420 : if (next == script->main())
373 9 : sp->put("main:\n");
374 147420 : if (pc != NULL) {
375 0 : if (pc == next)
376 0 : sp->put("--> ");
377 : else
378 0 : sp->put(" ");
379 : }
380 147420 : len = js_Disassemble1(cx, script, next, next - script->code, lines, sp);
381 147420 : if (!len)
382 0 : return JS_FALSE;
383 147420 : next += len;
384 : }
385 9 : return JS_TRUE;
386 : }
387 :
388 : JS_FRIEND_API(JSBool)
389 9 : js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp)
390 : {
391 9 : return js_DisassembleAtPC(cx, script, lines, NULL, sp);
392 : }
393 :
394 : JS_FRIEND_API(JSBool)
395 0 : js_DumpPC(JSContext *cx)
396 : {
397 0 : Sprinter sprinter(cx);
398 0 : if (!sprinter.init())
399 0 : return JS_FALSE;
400 0 : JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter);
401 0 : fprintf(stdout, "%s", sprinter.string());
402 0 : return ok;
403 : }
404 :
405 : JSBool
406 0 : js_DumpScript(JSContext *cx, JSScript *script)
407 : {
408 0 : Sprinter sprinter(cx);
409 0 : if (!sprinter.init())
410 0 : return JS_FALSE;
411 0 : JSBool ok = js_Disassemble(cx, script, true, &sprinter);
412 0 : fprintf(stdout, "%s", sprinter.string());
413 0 : return ok;
414 : }
415 :
416 : static char *
417 : QuoteString(Sprinter *sp, JSString *str, uint32_t quote);
418 :
419 : static bool
420 18432 : ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
421 : {
422 18432 : if (JSVAL_IS_STRING(v)) {
423 18 : Sprinter sprinter(cx);
424 9 : if (!sprinter.init())
425 0 : return false;
426 9 : char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"');
427 9 : if (!nbytes)
428 0 : return false;
429 9 : nbytes = JS_sprintf_append(NULL, "%s", nbytes);
430 9 : if (!nbytes)
431 0 : return false;
432 9 : bytes->initBytes(nbytes);
433 9 : return true;
434 : }
435 :
436 18423 : if (cx->runtime->gcRunning || cx->runtime->noGCOrAllocationCheck) {
437 0 : char *source = JS_sprintf_append(NULL, "<value>");
438 0 : if (!source)
439 0 : return false;
440 0 : bytes->initBytes(source);
441 0 : return true;
442 : }
443 :
444 18423 : if (!JSVAL_IS_PRIMITIVE(v)) {
445 18423 : JSObject *obj = JSVAL_TO_OBJECT(v);
446 18423 : if (obj->isBlock()) {
447 18423 : char *source = JS_sprintf_append(NULL, "depth %d {", obj->asBlock().stackDepth());
448 18423 : if (!source)
449 0 : return false;
450 :
451 18423 : Shape::Range r = obj->lastProperty()->all();
452 55269 : while (!r.empty()) {
453 18423 : const Shape &shape = r.front();
454 18423 : JSAtom *atom = JSID_IS_INT(shape.propid())
455 : ? cx->runtime->atomState.emptyAtom
456 18423 : : JSID_TO_ATOM(shape.propid());
457 :
458 36846 : JSAutoByteString bytes;
459 18423 : if (!js_AtomToPrintableString(cx, atom, &bytes))
460 0 : return false;
461 :
462 18423 : r.popFront();
463 : source = JS_sprintf_append(source, "%s: %d%s",
464 18423 : bytes.ptr(), shape.shortid(),
465 36846 : !r.empty() ? ", " : "");
466 18423 : if (!source)
467 0 : return false;
468 : }
469 :
470 18423 : source = JS_sprintf_append(source, "}");
471 18423 : if (!source)
472 0 : return false;
473 18423 : bytes->initBytes(source);
474 18423 : return true;
475 : }
476 :
477 0 : if (obj->isFunction()) {
478 0 : JSString *str = JS_DecompileFunction(cx, obj->toFunction(), JS_DONT_PRETTY_PRINT);
479 0 : if (!str)
480 0 : return false;
481 0 : return bytes->encode(cx, str);
482 : }
483 :
484 0 : if (obj->isRegExp()) {
485 0 : JSString *source = obj->asRegExp().toString(cx);
486 0 : if (!source)
487 0 : return false;
488 0 : JS::Anchor<JSString *> anchor(source);
489 0 : return bytes->encode(cx, source);
490 : }
491 : }
492 :
493 0 : return !!js_ValueToPrintable(cx, v, bytes, true);
494 : }
495 :
496 : JS_FRIEND_API(unsigned)
497 147420 : js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
498 : unsigned loc, JSBool lines, Sprinter *sp)
499 : {
500 147420 : JSOp op = (JSOp)*pc;
501 147420 : if (op >= JSOP_LIMIT) {
502 : char numBuf1[12], numBuf2[12];
503 0 : JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
504 0 : JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
505 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
506 0 : JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
507 0 : return 0;
508 : }
509 147420 : const JSCodeSpec *cs = &js_CodeSpec[op];
510 147420 : ptrdiff_t len = (ptrdiff_t) cs->length;
511 147420 : Sprint(sp, "%05u:", loc);
512 147420 : if (lines)
513 0 : Sprint(sp, "%4u", JS_PCToLineNumber(cx, script, pc));
514 147420 : Sprint(sp, " %s", js_CodeName[op]);
515 :
516 147420 : switch (JOF_TYPE(cs->format)) {
517 : case JOF_BYTE:
518 : // Scan the trynotes to find the associated catch block
519 : // and make the try opcode look like a jump instruction
520 : // with an offset. This simplifies code coverage analysis
521 : // based on this disassembled output.
522 55278 : if (op == JSOP_TRY) {
523 18423 : JSTryNoteArray *trynotes = script->trynotes();
524 : uint32_t i;
525 18865152 : for(i = 0; i < trynotes->length; i++) {
526 18865152 : JSTryNote note = trynotes->vector[i];
527 18865152 : if (note.kind == JSTRY_CATCH && note.start == loc + 1) {
528 : Sprint(sp, " %u (%+d)",
529 : (unsigned int) (loc+note.length+1),
530 18423 : (int) (note.length+1));
531 18423 : break;
532 : }
533 : }
534 : }
535 55278 : break;
536 :
537 : case JOF_JUMP: {
538 36855 : ptrdiff_t off = GET_JUMP_OFFSET(pc);
539 36855 : Sprint(sp, " %u (%+d)", loc + (int) off, (int) off);
540 36855 : break;
541 : }
542 :
543 : case JOF_ATOM: {
544 9 : Value v = StringValue(script->getAtom(GET_UINT32_INDEX(pc)));
545 18 : JSAutoByteString bytes;
546 9 : if (!ToDisassemblySource(cx, v, &bytes))
547 0 : return 0;
548 9 : Sprint(sp, " %s", bytes.ptr());
549 9 : break;
550 : }
551 :
552 : case JOF_DOUBLE: {
553 0 : Value v = script->getConst(GET_UINT32_INDEX(pc));
554 0 : JSAutoByteString bytes;
555 0 : if (!ToDisassemblySource(cx, v, &bytes))
556 0 : return 0;
557 0 : Sprint(sp, " %s", bytes.ptr());
558 0 : break;
559 : }
560 :
561 : case JOF_OBJECT: {
562 : /* Don't call obj.toSource if analysis/inference is active. */
563 18423 : if (cx->compartment->activeAnalysis) {
564 0 : Sprint(sp, " object");
565 0 : break;
566 : }
567 :
568 18423 : JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
569 : {
570 36846 : JSAutoByteString bytes;
571 18423 : if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
572 0 : return 0;
573 36846 : Sprint(sp, " %s", bytes.ptr());
574 : }
575 18423 : break;
576 : }
577 :
578 : case JOF_REGEXP: {
579 0 : JSObject *obj = script->getRegExp(GET_UINT32_INDEX(pc));
580 0 : JSAutoByteString bytes;
581 0 : if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
582 0 : return 0;
583 0 : Sprint(sp, " %s", bytes.ptr());
584 0 : break;
585 : }
586 :
587 : case JOF_TABLESWITCH:
588 : {
589 : int32_t i, low, high;
590 :
591 9 : ptrdiff_t off = GET_JUMP_OFFSET(pc);
592 9 : jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
593 9 : low = GET_JUMP_OFFSET(pc2);
594 9 : pc2 += JUMP_OFFSET_LEN;
595 9 : high = GET_JUMP_OFFSET(pc2);
596 9 : pc2 += JUMP_OFFSET_LEN;
597 9 : Sprint(sp, " defaultOffset %d low %d high %d", int(off), low, high);
598 9 : for (i = low; i <= high; i++) {
599 0 : off = GET_JUMP_OFFSET(pc2);
600 0 : Sprint(sp, "\n\t%d: %d", i, int(off));
601 0 : pc2 += JUMP_OFFSET_LEN;
602 : }
603 9 : len = 1 + pc2 - pc;
604 9 : break;
605 : }
606 :
607 : case JOF_LOOKUPSWITCH:
608 : {
609 : jsatomid npairs;
610 :
611 0 : ptrdiff_t off = GET_JUMP_OFFSET(pc);
612 0 : jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
613 0 : npairs = GET_UINT16(pc2);
614 0 : pc2 += UINT16_LEN;
615 0 : Sprint(sp, " offset %d npairs %u", int(off), unsigned(npairs));
616 0 : while (npairs) {
617 0 : uint32_t constIndex = GET_UINT32_INDEX(pc2);
618 0 : pc2 += UINT32_INDEX_LEN;
619 0 : off = GET_JUMP_OFFSET(pc2);
620 0 : pc2 += JUMP_OFFSET_LEN;
621 :
622 0 : JSAutoByteString bytes;
623 0 : if (!ToDisassemblySource(cx, script->getConst(constIndex), &bytes))
624 0 : return 0;
625 0 : Sprint(sp, "\n\t%s: %d", bytes.ptr(), int(off));
626 0 : npairs--;
627 : }
628 0 : len = 1 + pc2 - pc;
629 0 : break;
630 : }
631 :
632 : case JOF_QARG:
633 0 : Sprint(sp, " %u", GET_ARGNO(pc));
634 0 : break;
635 :
636 : case JOF_LOCAL:
637 18423 : Sprint(sp, " %u", GET_SLOTNO(pc));
638 18423 : break;
639 :
640 : case JOF_SLOTOBJECT: {
641 0 : Sprint(sp, " %u", GET_SLOTNO(pc));
642 0 : JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + SLOTNO_LEN));
643 0 : JSAutoByteString bytes;
644 0 : if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
645 0 : return 0;
646 0 : Sprint(sp, " %s", bytes.ptr());
647 0 : break;
648 : }
649 :
650 : {
651 : int i;
652 :
653 : case JOF_UINT16PAIR:
654 0 : i = (int)GET_UINT16(pc);
655 0 : Sprint(sp, " %d", i);
656 0 : pc += UINT16_LEN;
657 : /* FALL THROUGH */
658 :
659 : case JOF_UINT16:
660 18423 : i = (int)GET_UINT16(pc);
661 18423 : goto print_int;
662 :
663 : case JOF_UINT24:
664 0 : JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
665 0 : i = (int)GET_UINT24(pc);
666 0 : goto print_int;
667 :
668 : case JOF_UINT8:
669 0 : i = GET_UINT8(pc);
670 0 : goto print_int;
671 :
672 : case JOF_INT8:
673 0 : i = GET_INT8(pc);
674 0 : goto print_int;
675 :
676 : case JOF_INT32:
677 0 : JS_ASSERT(op == JSOP_INT32);
678 0 : i = GET_INT32(pc);
679 : print_int:
680 18423 : Sprint(sp, " %d", i);
681 18423 : break;
682 : }
683 :
684 : default: {
685 : char numBuf[12];
686 0 : JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
687 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
688 0 : JSMSG_UNKNOWN_FORMAT, numBuf);
689 0 : return 0;
690 : }
691 : }
692 147420 : sp->put("\n");
693 147420 : return len;
694 : }
695 :
696 : #endif /* DEBUG */
697 :
698 : /************************************************************************/
699 :
700 : const size_t Sprinter::DefaultSize = 64;
701 :
702 : bool
703 6512 : Sprinter::realloc_(size_t newSize)
704 : {
705 6512 : JS_ASSERT(newSize > (size_t) offset);
706 6512 : char *newBuf = (char *) context->realloc_(base, newSize);
707 6512 : if (!newBuf)
708 0 : return false;
709 6512 : base = newBuf;
710 6512 : size = newSize;
711 6512 : base[size - 1] = 0;
712 6512 : return true;
713 : }
714 :
715 59404 : Sprinter::Sprinter(JSContext *cx)
716 : : context(cx),
717 : #ifdef DEBUG
718 : initialized(false),
719 : #endif
720 59404 : base(NULL), size(0), offset(0)
721 59404 : { }
722 :
723 59404 : Sprinter::~Sprinter()
724 : {
725 : #ifdef DEBUG
726 59404 : if (initialized)
727 50927 : checkInvariants();
728 : #endif
729 59404 : context->free_(base);
730 59404 : }
731 :
732 : bool
733 50927 : Sprinter::init()
734 : {
735 50927 : JS_ASSERT(!initialized);
736 50927 : base = (char *) context->malloc_(DefaultSize);
737 50927 : if (!base)
738 0 : return false;
739 : #ifdef DEBUG
740 50927 : initialized = true;
741 : #endif
742 50927 : *base = 0;
743 50927 : size = DefaultSize;
744 50927 : base[size - 1] = 0;
745 50927 : return true;
746 : }
747 :
748 : void
749 4910885 : Sprinter::checkInvariants() const
750 : {
751 4910885 : JS_ASSERT(initialized);
752 4910885 : JS_ASSERT((size_t) offset < size);
753 4910885 : JS_ASSERT(base[size - 1] == 0);
754 4910885 : }
755 :
756 : const char *
757 15504 : Sprinter::string() const
758 : {
759 15504 : return base;
760 : }
761 :
762 : const char *
763 0 : Sprinter::stringEnd() const
764 : {
765 0 : return base + offset;
766 : }
767 :
768 : char *
769 200179 : Sprinter::stringAt(ptrdiff_t off) const
770 : {
771 200179 : JS_ASSERT(off >= 0 && (size_t) off < size);
772 200179 : return base + off;
773 : }
774 :
775 : char &
776 405120 : Sprinter::operator[](size_t off)
777 : {
778 405120 : JS_ASSERT(off >= 0 && (size_t) off < size);
779 405120 : return *(base + off);
780 : }
781 :
782 : bool
783 126 : Sprinter::empty() const
784 : {
785 126 : return *base == 0;
786 : }
787 :
788 : char *
789 1306731 : Sprinter::reserve(size_t len)
790 : {
791 2613462 : InvariantChecker ic(this);
792 :
793 1306731 : while (len + 1 > size - offset) { /* Include trailing \0 */
794 6512 : if (!realloc_(size * 2))
795 0 : return NULL;
796 : }
797 :
798 1306731 : char *sb = base + offset;
799 1306731 : offset += len;
800 1306731 : return sb;
801 : }
802 :
803 : char *
804 93837 : Sprinter::reserveAndClear(size_t len)
805 : {
806 93837 : char *sb = reserve(len);
807 93837 : if (sb)
808 93837 : memset(sb, 0, len);
809 93837 : return sb;
810 : }
811 :
812 : ptrdiff_t
813 1122816 : Sprinter::put(const char *s, size_t len)
814 : {
815 2245632 : InvariantChecker ic(this);
816 :
817 1122816 : const char *oldBase = base;
818 1122816 : const char *oldEnd = base + size;
819 :
820 1122816 : ptrdiff_t oldOffset = offset;
821 1122816 : char *bp = reserve(len);
822 1122816 : if (!bp)
823 0 : return -1;
824 :
825 : /* s is within the buffer already */
826 1122816 : if (s >= oldBase && s < oldEnd) {
827 : /* buffer was realloc'ed */
828 20712 : if (base != oldBase)
829 176 : s = stringAt(s - oldBase); /* this is where it lives now */
830 20712 : memmove(bp, s, len);
831 : } else {
832 1102104 : js_memcpy(bp, s, len);
833 : }
834 :
835 1122816 : bp[len] = 0;
836 1122816 : return oldOffset;
837 : }
838 :
839 : ptrdiff_t
840 983935 : Sprinter::put(const char *s)
841 : {
842 983935 : return put(s, strlen(s));
843 : }
844 :
845 : ptrdiff_t
846 432 : Sprinter::putString(JSString *s)
847 : {
848 864 : InvariantChecker ic(this);
849 :
850 432 : size_t length = s->length();
851 432 : const jschar *chars = s->getChars(context);
852 432 : if (!chars)
853 0 : return -1;
854 :
855 432 : size_t size = GetDeflatedStringLength(context, chars, length);
856 432 : if (size == (size_t) -1)
857 0 : return -1;
858 :
859 432 : ptrdiff_t oldOffset = offset;
860 432 : char *buffer = reserve(size);
861 432 : if (!buffer)
862 0 : return -1;
863 432 : DeflateStringToBuffer(context, chars, length, buffer, &size);
864 432 : buffer[size] = 0;
865 :
866 432 : return oldOffset;
867 : }
868 :
869 : int
870 0 : Sprinter::printf(const char *fmt, ...)
871 : {
872 0 : InvariantChecker ic(this);
873 :
874 0 : do {
875 : va_list va;
876 0 : va_start(va, fmt);
877 0 : int i = vsnprintf(base + offset, size - offset, fmt, va);
878 0 : va_end(va);
879 :
880 0 : if (i > -1 && (size_t) i < size - offset) {
881 0 : offset += i;
882 0 : return i;
883 : }
884 0 : } while (realloc_(size * 2));
885 :
886 0 : return -1;
887 : }
888 :
889 : void
890 21641 : Sprinter::setOffset(const char *end)
891 : {
892 21641 : JS_ASSERT(end >= base && end < base + size);
893 21641 : offset = end - base;
894 21641 : }
895 :
896 : void
897 110488 : Sprinter::setOffset(ptrdiff_t off)
898 : {
899 110488 : JS_ASSERT(off >= 0 && (size_t) off < size);
900 110488 : offset = off;
901 110488 : }
902 :
903 : ptrdiff_t
904 372565 : Sprinter::getOffset() const
905 : {
906 372565 : return offset;
907 : }
908 :
909 : ptrdiff_t
910 12734 : Sprinter::getOffsetOf(const char *string) const
911 : {
912 12734 : JS_ASSERT(string >= base && string < base + size);
913 12734 : return string - base;
914 : }
915 :
916 : ptrdiff_t
917 740033 : js::Sprint(Sprinter *sp, const char *format, ...)
918 : {
919 : va_list ap;
920 : char *bp;
921 : ptrdiff_t offset;
922 :
923 740033 : va_start(ap, format);
924 740033 : bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
925 740033 : va_end(ap);
926 740033 : if (!bp) {
927 0 : JS_ReportOutOfMemory(sp->context);
928 0 : return -1;
929 : }
930 740033 : offset = sp->put(bp);
931 740033 : sp->context->free_(bp);
932 740033 : return offset;
933 : }
934 :
935 : const char js_EscapeMap[] = {
936 : '\b', 'b',
937 : '\f', 'f',
938 : '\n', 'n',
939 : '\r', 'r',
940 : '\t', 't',
941 : '\v', 'v',
942 : '"', '"',
943 : '\'', '\'',
944 : '\\', '\\',
945 : '\0'
946 : };
947 :
948 : #define DONT_ESCAPE 0x10000
949 :
950 : static char *
951 91397 : QuoteString(Sprinter *sp, JSString *str, uint32_t quote)
952 : {
953 : /* Sample off first for later return value pointer computation. */
954 91397 : JSBool dontEscape = (quote & DONT_ESCAPE) != 0;
955 91397 : jschar qc = (jschar) quote;
956 91397 : ptrdiff_t offset = sp->getOffset();
957 91397 : if (qc && Sprint(sp, "%c", (char)qc) < 0)
958 0 : return NULL;
959 :
960 91397 : const jschar *s = str->getChars(sp->context);
961 91397 : if (!s)
962 0 : return NULL;
963 91397 : const jschar *z = s + str->length();
964 :
965 : /* Loop control variables: z points at end of string sentinel. */
966 91937 : for (const jschar *t = s; t < z; s = ++t) {
967 : /* Move t forward from s past un-quote-worthy characters. */
968 89619 : jschar c = *t;
969 384681 : while (c < 127 && isprint(c) && c != qc && c != '\\' && c != '\t') {
970 294522 : c = *++t;
971 294522 : if (t == z)
972 89079 : break;
973 : }
974 :
975 : {
976 89619 : ptrdiff_t len = t - s;
977 89619 : ptrdiff_t base = sp->getOffset();
978 89619 : char *bp = sp->reserve(len);
979 89619 : if (!bp)
980 0 : return NULL;
981 :
982 384141 : for (ptrdiff_t i = 0; i < len; ++i)
983 294522 : (*sp)[base + i] = (char) *s++;
984 89619 : (*sp)[base + len] = 0;
985 : }
986 :
987 89619 : if (t == z)
988 89079 : break;
989 :
990 : /* Use js_EscapeMap, \u, or \x only if necessary. */
991 : bool ok;
992 : const char *e;
993 540 : if (!(c >> 8) && c != 0 && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
994 : ok = dontEscape
995 0 : ? Sprint(sp, "%c", (char)c) >= 0
996 482 : : Sprint(sp, "\\%c", e[1]) >= 0;
997 : } else {
998 : /*
999 : * Use \x only if the high byte is 0 and we're in a quoted string,
1000 : * because ECMA-262 allows only \u, not \x, in Unicode identifiers
1001 : * (see bug 621814).
1002 : */
1003 58 : ok = Sprint(sp, (qc && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c) >= 0;
1004 : }
1005 540 : if (!ok)
1006 0 : return NULL;
1007 : }
1008 :
1009 : /* Sprint the closing quote and return the quoted string. */
1010 91397 : if (qc && Sprint(sp, "%c", (char)qc) < 0)
1011 0 : return NULL;
1012 :
1013 : /*
1014 : * If we haven't Sprint'd anything yet, Sprint an empty string so that
1015 : * the return below gives a valid result.
1016 : */
1017 91397 : if (offset == sp->getOffset() && Sprint(sp, "") < 0)
1018 0 : return NULL;
1019 :
1020 91397 : return sp->stringAt(offset);
1021 : }
1022 :
1023 : JSString *
1024 24346 : js_QuoteString(JSContext *cx, JSString *str, jschar quote)
1025 : {
1026 48692 : Sprinter sprinter(cx);
1027 24346 : if (!sprinter.init())
1028 0 : return NULL;
1029 24346 : char *bytes = QuoteString(&sprinter, str, quote);
1030 24346 : JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
1031 24346 : return escstr;
1032 : }
1033 :
1034 : /************************************************************************/
1035 :
1036 : /*
1037 : * Information for associating the decompilation of each opcode in a script
1038 : * with the place where it appears in the text for the decompilation of the
1039 : * entire script (or the function containing the script).
1040 : */
1041 : struct DecompiledOpcode
1042 0 : {
1043 : /* Decompiled text of this opcode. */
1044 : const char *text;
1045 :
1046 : /* Bytecode into which this opcode was nested, or NULL. */
1047 : jsbytecode *parent;
1048 :
1049 : /*
1050 : * Offset into the parent's decompiled text of the decompiled text of this
1051 : * opcode. For opcodes with a NULL parent, this was emitted directly into
1052 : * the permanent output at the given offset.
1053 : */
1054 : int32_t parentOffset;
1055 :
1056 : /*
1057 : * Surrounded by parentheses when printed, which parentOffset does not
1058 : * account for.
1059 : */
1060 : bool parenthesized;
1061 :
1062 0 : DecompiledOpcode()
1063 0 : : text(NULL), parent(NULL), parentOffset(-1), parenthesized(false)
1064 0 : {}
1065 : };
1066 :
1067 : struct JSPrinter
1068 : {
1069 : Sprinter sprinter; /* base class state */
1070 : LifoAlloc pool; /* string allocation pool */
1071 : unsigned indent; /* indentation in spaces */
1072 : bool pretty; /* pretty-print: indent, use newlines */
1073 : bool grouped; /* in parenthesized expression context */
1074 : bool strict; /* in code marked strict */
1075 : JSScript *script; /* script being printed */
1076 : jsbytecode *dvgfence; /* DecompileExpression fencepost */
1077 : jsbytecode **pcstack; /* DecompileExpression modeled stack */
1078 : JSFunction *fun; /* interpreted function */
1079 : Vector<JSAtom *> *localNames; /* argument and variable names */
1080 : Vector<DecompiledOpcode> *decompiledOpcodes; /* optional state for decompiled ops */
1081 :
1082 0 : DecompiledOpcode &decompiled(jsbytecode *pc) {
1083 0 : JS_ASSERT(decompiledOpcodes);
1084 0 : return (*decompiledOpcodes)[pc - script->code];
1085 : }
1086 : };
1087 :
1088 : JSPrinter *
1089 15458 : js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
1090 : unsigned indent, JSBool pretty, JSBool grouped, JSBool strict)
1091 : {
1092 15458 : JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter));
1093 15458 : if (!jp)
1094 0 : return NULL;
1095 15458 : new (&jp->sprinter) Sprinter(cx);
1096 15458 : if (!jp->sprinter.init())
1097 0 : return NULL;
1098 15458 : new (&jp->pool) LifoAlloc(1024);
1099 15458 : jp->indent = indent;
1100 15458 : jp->pretty = !!pretty;
1101 15458 : jp->grouped = !!grouped;
1102 15458 : jp->strict = !!strict;
1103 15458 : jp->script = NULL;
1104 15458 : jp->dvgfence = NULL;
1105 15458 : jp->pcstack = NULL;
1106 15458 : jp->fun = fun;
1107 15458 : jp->localNames = NULL;
1108 15458 : jp->decompiledOpcodes = NULL;
1109 15458 : if (fun && fun->isInterpreted() && fun->script()->bindings.hasLocalNames()) {
1110 9209 : jp->localNames = cx->new_<Vector<JSAtom *> >(cx);
1111 9209 : if (!jp->localNames || !fun->script()->bindings.getLocalNameArray(cx, jp->localNames)) {
1112 0 : js_DestroyPrinter(jp);
1113 0 : return NULL;
1114 : }
1115 : }
1116 15458 : return jp;
1117 : }
1118 :
1119 : void
1120 15458 : js_DestroyPrinter(JSPrinter *jp)
1121 : {
1122 15458 : JSContext *cx = jp->sprinter.context;
1123 15458 : jp->pool.freeAll();
1124 15458 : Foreground::delete_(jp->localNames);
1125 15458 : jp->sprinter.Sprinter::~Sprinter();
1126 15458 : cx->free_(jp);
1127 15458 : }
1128 :
1129 : JSString *
1130 12884 : js_GetPrinterOutput(JSPrinter *jp)
1131 : {
1132 12884 : JSContext *cx = jp->sprinter.context;
1133 12884 : return JS_NewStringCopyZ(cx, jp->sprinter.string());
1134 : }
1135 :
1136 : /* Mark the parent and offset into the parent's text for a printed opcode. */
1137 : static inline void
1138 49334 : UpdateDecompiledParent(JSPrinter *jp, jsbytecode *pc, jsbytecode *parent, size_t offset)
1139 : {
1140 49334 : if (jp->decompiledOpcodes && pc) {
1141 0 : jp->decompiled(pc).parent = parent;
1142 0 : jp->decompiled(pc).parentOffset = offset;
1143 : }
1144 49334 : }
1145 :
1146 : /*
1147 : * NB: Indexed by SRC_DECL_* defines from frontend/BytecodeEmitter.h.
1148 : */
1149 : static const char * const var_prefix[] = {"var ", "const ", "let "};
1150 :
1151 : static const char *
1152 32442 : VarPrefix(jssrcnote *sn)
1153 : {
1154 32442 : if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
1155 5560 : ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
1156 5560 : if ((unsigned)type <= SRC_DECL_LET)
1157 4993 : return var_prefix[type];
1158 : }
1159 27449 : return "";
1160 : }
1161 :
1162 : int
1163 117830 : js_printf(JSPrinter *jp, const char *format, ...)
1164 : {
1165 : va_list ap;
1166 : char *bp, *fp;
1167 : int cc;
1168 :
1169 117830 : if (*format == '\0')
1170 0 : return 0;
1171 :
1172 117830 : va_start(ap, format);
1173 :
1174 : /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
1175 117830 : if (*format == '\t') {
1176 45208 : format++;
1177 45208 : if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) {
1178 0 : va_end(ap);
1179 0 : return -1;
1180 : }
1181 : }
1182 :
1183 : /* Suppress newlines (must be once per format, at the end) if not pretty. */
1184 117830 : fp = NULL;
1185 117830 : if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
1186 38255 : fp = JS_strdup(jp->sprinter.context, format);
1187 38255 : if (!fp) {
1188 0 : va_end(ap);
1189 0 : return -1;
1190 : }
1191 38255 : fp[cc] = '\0';
1192 38255 : format = fp;
1193 : }
1194 :
1195 : /* Allocate temp space, convert format, and put. */
1196 117830 : bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
1197 117830 : if (fp) {
1198 38255 : jp->sprinter.context->free_(fp);
1199 38255 : format = NULL;
1200 : }
1201 117830 : if (!bp) {
1202 0 : JS_ReportOutOfMemory(jp->sprinter.context);
1203 0 : va_end(ap);
1204 0 : return -1;
1205 : }
1206 :
1207 117830 : cc = strlen(bp);
1208 117830 : if (jp->sprinter.put(bp, (size_t)cc) < 0)
1209 0 : cc = -1;
1210 117830 : jp->sprinter.context->free_(bp);
1211 :
1212 117830 : va_end(ap);
1213 117830 : return cc;
1214 : }
1215 :
1216 : JSBool
1217 29047 : js_puts(JSPrinter *jp, const char *s)
1218 : {
1219 29047 : return jp->sprinter.put(s) >= 0;
1220 : }
1221 :
1222 : /************************************************************************/
1223 :
1224 : struct SprintStack
1225 19573 : {
1226 : Sprinter sprinter; /* sprinter for postfix to infix buffering */
1227 : ptrdiff_t *offsets; /* stack of postfix string offsets */
1228 : jsbytecode *opcodes; /* parallel stack of JS opcodes */
1229 : jsbytecode **bytecodes; /* actual script bytecode pushing the value */
1230 : unsigned top; /* top of stack index */
1231 : unsigned inArrayInit; /* array initialiser/comprehension level */
1232 : JSBool inGenExp; /* in generator expression */
1233 : JSPrinter *printer; /* permanent output goes here */
1234 :
1235 19573 : explicit SprintStack(JSContext *cx)
1236 : : sprinter(cx), offsets(NULL),
1237 : opcodes(NULL), bytecodes(NULL), top(0), inArrayInit(0),
1238 19573 : inGenExp(JS_FALSE), printer(NULL)
1239 19573 : { }
1240 : };
1241 :
1242 : /*
1243 : * Set the decompiled text of an opcode, according to an offset into the
1244 : * print stack's sprinter buffer.
1245 : */
1246 : static inline bool
1247 72759 : UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo)
1248 : {
1249 72759 : JSPrinter *jp = ss->printer;
1250 :
1251 72759 : if (jp->decompiledOpcodes && jp->decompiled(pc).text == NULL) {
1252 0 : const char *text = ss->sprinter.stringAt(todo);
1253 0 : size_t len = strlen(text) + 1;
1254 :
1255 0 : char *ntext = ss->printer->pool.newArrayUninitialized<char>(len);
1256 0 : if (!ntext) {
1257 0 : js_ReportOutOfMemory(ss->sprinter.context);
1258 0 : return false;
1259 : }
1260 :
1261 0 : js_memcpy(ntext, text, len);
1262 0 : jp->decompiled(pc).text = const_cast<const char *>(ntext);
1263 : }
1264 :
1265 72759 : return true;
1266 : }
1267 :
1268 : static inline const char *
1269 27682 : SprintDupeStr(SprintStack *ss, const char *str)
1270 : {
1271 27682 : size_t len = strlen(str) + 1;
1272 :
1273 27682 : const char *nstr = ss->printer->pool.newArrayUninitialized<char>(len);
1274 27682 : if (nstr) {
1275 27682 : js_memcpy((char *) nstr, str, len);
1276 : } else {
1277 0 : js_ReportOutOfMemory(ss->sprinter.context);
1278 0 : nstr = "";
1279 : }
1280 :
1281 27682 : return nstr;
1282 : }
1283 :
1284 : /* Place an opcode's decompiled text into a printer's permanent output. */
1285 : static inline void
1286 17105 : SprintOpcodePermanent(JSPrinter *jp, const char *str, jsbytecode *pc)
1287 : {
1288 17105 : ptrdiff_t offset = jp->sprinter.getOffset();
1289 17105 : UpdateDecompiledParent(jp, pc, NULL, offset);
1290 17105 : js_printf(jp, "%s", str);
1291 17105 : }
1292 :
1293 : /*
1294 : * Place an opcode's decompiled text into the printed output for another
1295 : * opcode parentpc, where startOffset indicates the printer offset for the
1296 : * start of parentpc.
1297 : */
1298 : static inline void
1299 30949 : SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc,
1300 : jsbytecode *parentpc, ptrdiff_t startOffset)
1301 : {
1302 30949 : if (startOffset < 0) {
1303 0 : JS_ASSERT(ss->sprinter.context->isExceptionPending());
1304 0 : return;
1305 : }
1306 30949 : ptrdiff_t offset = ss->sprinter.getOffset();
1307 30949 : UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset);
1308 30949 : ss->sprinter.put(str);
1309 : }
1310 :
1311 : /*
1312 : * Copy the decompiled text for an opcode to all other ops which it was
1313 : * decomposed into.
1314 : */
1315 : static inline void
1316 77 : CopyDecompiledTextForDecomposedOp(JSPrinter *jp, jsbytecode *pc)
1317 : {
1318 77 : JS_ASSERT(js_CodeSpec[*pc].format & JOF_DECOMPOSE);
1319 :
1320 77 : if (jp->decompiledOpcodes) {
1321 0 : size_t len = GetDecomposeLength(pc, js_CodeSpec[*pc].length);
1322 :
1323 0 : const char *text = jp->decompiled(pc).text;
1324 :
1325 0 : jsbytecode *pc2 = pc + GetBytecodeLength(pc);
1326 0 : for (; pc2 < pc + len; pc2 += GetBytecodeLength(pc2)) {
1327 0 : jp->decompiled(pc2).text = text;
1328 0 : jp->decompiled(pc2).parent = pc;
1329 0 : jp->decompiled(pc2).parentOffset = 0;
1330 : }
1331 : }
1332 77 : }
1333 :
1334 : /*
1335 : * Find the depth of the operand stack when the interpreter reaches the given
1336 : * pc in script. pcstack must have space for least script->depth elements. On
1337 : * return it will contain pointers to opcodes that populated the interpreter's
1338 : * current operand stack.
1339 : *
1340 : * This function cannot raise an exception or error. However, due to a risk of
1341 : * potential bugs when modeling the stack, the function returns -1 if it
1342 : * detects an inconsistency in the model. Such an inconsistency triggers an
1343 : * assert in a debug build.
1344 : */
1345 : static int
1346 : ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
1347 : jsbytecode **pcstack, jsbytecode **lastDecomposedPC);
1348 :
1349 : #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
1350 :
1351 : /*
1352 : * Decompile a part of expression up to the given pc. The function returns
1353 : * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
1354 : * the decompiler fails due to a bug and/or unimplemented feature, or the
1355 : * decompiled string on success.
1356 : */
1357 : static char *
1358 : DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
1359 : jsbytecode *pc);
1360 :
1361 : /*
1362 : * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
1363 : * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
1364 : * decompile the code that generated the missing value. This is used when
1365 : * reporting errors, where the model stack will lack |pcdepth| non-negative
1366 : * offsets (see DecompileExpression and DecompileCode).
1367 : *
1368 : * If the stacked offset is -1, return 0 to index the NUL padding at the start
1369 : * of ss->sprinter.base. If this happens, it means there is a decompiler bug
1370 : * to fix, but it won't violate memory safety.
1371 : */
1372 : static ptrdiff_t
1373 98660 : GetOff(SprintStack *ss, unsigned i)
1374 : {
1375 : ptrdiff_t off;
1376 : jsbytecode *pc;
1377 : char *bytes;
1378 :
1379 98660 : off = ss->offsets[i];
1380 98660 : if (off >= 0)
1381 98604 : return off;
1382 :
1383 56 : JS_ASSERT(ss->printer->pcstack);
1384 56 : if (off <= -2 && ss->printer->pcstack) {
1385 47 : pc = ss->printer->pcstack[-2 - off];
1386 : bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
1387 47 : ss->printer->fun, pc);
1388 47 : if (!bytes)
1389 0 : return 0;
1390 47 : if (bytes != FAILED_EXPRESSION_DECOMPILER) {
1391 10 : off = ss->sprinter.put(bytes);
1392 10 : if (off < 0)
1393 0 : off = 0;
1394 10 : ss->offsets[i] = off;
1395 10 : ss->sprinter.context->free_(bytes);
1396 10 : return off;
1397 : }
1398 :
1399 37 : if (!*ss->sprinter.string()) {
1400 37 : memset(ss->sprinter.stringAt(0), 0, ss->sprinter.getOffset());
1401 37 : ss->offsets[i] = -1;
1402 : }
1403 : }
1404 46 : return 0;
1405 : }
1406 :
1407 : static const char *
1408 8322 : GetStr(SprintStack *ss, unsigned i)
1409 : {
1410 8322 : ptrdiff_t off = GetOff(ss, i);
1411 8322 : return ss->sprinter.stringAt(off);
1412 : }
1413 :
1414 : /*
1415 : * Gap between stacked strings to allow for insertion of parens and commas
1416 : * when auto-parenthesizing expressions and decompiling array initialisers.
1417 : */
1418 : #define PAREN_SLOP (2 + 1)
1419 :
1420 : /* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */
1421 : JS_STATIC_ASSERT(JSOP_FAKE_LIMIT <= 255);
1422 :
1423 : static inline void
1424 93837 : AddParenSlop(SprintStack *ss)
1425 : {
1426 93837 : ss->sprinter.reserveAndClear(PAREN_SLOP);
1427 93837 : }
1428 :
1429 : static JSBool
1430 93828 : PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL)
1431 : {
1432 : unsigned top;
1433 :
1434 : /* ss->top points to the next free slot; be paranoid about overflow. */
1435 93828 : top = ss->top;
1436 93828 : JS_ASSERT(top < StackDepth(ss->printer->script));
1437 93828 : if (top >= StackDepth(ss->printer->script)) {
1438 0 : JS_ReportOutOfMemory(ss->sprinter.context);
1439 0 : return JS_FALSE;
1440 : }
1441 :
1442 : /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1443 93828 : ss->offsets[top] = off;
1444 93828 : ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
1445 : : (op == JSOP_GETELEM2) ? JSOP_GETELEM
1446 93828 : : op);
1447 93828 : ss->bytecodes[top] = pc;
1448 93828 : ss->top = ++top;
1449 :
1450 93828 : AddParenSlop(ss);
1451 93828 : return JS_TRUE;
1452 : }
1453 :
1454 : static bool
1455 1386 : PushStr(SprintStack *ss, const char *str, JSOp op)
1456 : {
1457 1386 : ptrdiff_t off = ss->sprinter.put(str);
1458 1386 : if (off < 0)
1459 0 : return false;
1460 1386 : return PushOff(ss, off, op);
1461 : }
1462 :
1463 : static ptrdiff_t
1464 83678 : PopOffPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
1465 : {
1466 : unsigned top;
1467 : const JSCodeSpec *topcs;
1468 : ptrdiff_t off;
1469 :
1470 83678 : if (ppc)
1471 50405 : *ppc = NULL;
1472 :
1473 : /* ss->top points to the next free slot; be paranoid about underflow. */
1474 83678 : top = ss->top;
1475 83678 : JS_ASSERT(top != 0);
1476 83678 : if (top == 0)
1477 0 : return 0;
1478 :
1479 83678 : ss->top = --top;
1480 83678 : off = GetOff(ss, top);
1481 83678 : topcs = &js_CodeSpec[ss->opcodes[top]];
1482 :
1483 83678 : jsbytecode *pc = ss->bytecodes[top];
1484 83678 : if (ppc)
1485 50405 : *ppc = pc;
1486 :
1487 83678 : if (topcs->prec != 0 && topcs->prec < prec) {
1488 585 : ss->offsets[top] = off - 2;
1489 585 : ss->sprinter.setOffset(off - 2);
1490 585 : off = Sprint(&ss->sprinter, "(%s)", ss->sprinter.stringAt(off));
1491 1170 : if (ss->printer->decompiledOpcodes && pc)
1492 0 : ss->printer->decompiled(pc).parenthesized = true;
1493 : } else {
1494 83093 : ss->sprinter.setOffset(off);
1495 : }
1496 83678 : return off;
1497 : }
1498 :
1499 : static const char *
1500 75538 : PopStrPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
1501 : {
1502 : ptrdiff_t off;
1503 :
1504 75538 : off = PopOffPrec(ss, prec, ppc);
1505 75538 : return ss->sprinter.stringAt(off);
1506 : }
1507 :
1508 : /*
1509 : * As for PopStrPrec, but duplicates the string into the printer's arena.
1510 : * Strings returned by PopStrPrec are otherwise invalidated if any new text
1511 : * is printed into ss.
1512 : */
1513 : static const char *
1514 19397 : PopStrPrecDupe(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
1515 : {
1516 19397 : const char *str = PopStrPrec(ss, prec, ppc);
1517 19397 : return SprintDupeStr(ss, str);
1518 : }
1519 :
1520 : static ptrdiff_t
1521 8140 : PopOff(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
1522 : {
1523 8140 : return PopOffPrec(ss, js_CodeSpec[op].prec, ppc);
1524 : }
1525 :
1526 : static const char *
1527 52690 : PopStr(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
1528 : {
1529 52690 : return PopStrPrec(ss, js_CodeSpec[op].prec, ppc);
1530 : }
1531 :
1532 : static const char *
1533 15946 : PopStrDupe(SprintStack *ss, JSOp op, jsbytecode **ppc = NULL)
1534 : {
1535 15946 : return PopStrPrecDupe(ss, js_CodeSpec[op].prec, ppc);
1536 : }
1537 :
1538 : /*
1539 : * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1540 : * extra parens around assignment, which avoids a strict-mode warning.
1541 : */
1542 : static const char *
1543 1358 : PopCondStr(SprintStack *ss, jsbytecode **ppc = NULL)
1544 : {
1545 1358 : JSOp op = (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET)
1546 : ? JSOP_IFEQ
1547 1358 : : JSOP_NOP;
1548 1358 : return PopStr(ss, op, ppc);
1549 : }
1550 :
1551 : static inline bool
1552 2354 : IsInitializerOp(unsigned char op)
1553 : {
1554 2354 : return op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT;
1555 : }
1556 :
1557 : struct TableEntry {
1558 : jsval key;
1559 : ptrdiff_t offset;
1560 : JSAtom *label;
1561 : int order; /* source order for stable tableswitch sort */
1562 : };
1563 :
1564 : inline bool
1565 27 : CompareTableEntries(const TableEntry &a, const TableEntry &b, bool *lessOrEqualp)
1566 : {
1567 27 : *lessOrEqualp = (a.offset != b.offset) ? a.offset <= b.offset : a.order <= b.order;
1568 27 : return true;
1569 : }
1570 :
1571 : static ptrdiff_t
1572 9 : SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
1573 : {
1574 : double d;
1575 : ptrdiff_t todo;
1576 : char *s;
1577 :
1578 9 : JS_ASSERT(JSVAL_IS_DOUBLE(v));
1579 9 : d = JSVAL_TO_DOUBLE(v);
1580 9 : if (JSDOUBLE_IS_NEGZERO(d)) {
1581 0 : todo = sp->put("-0");
1582 0 : *opp = JSOP_NEG;
1583 9 : } else if (!JSDOUBLE_IS_FINITE(d)) {
1584 : /* Don't use Infinity and NaN, as local variables may shadow them. */
1585 9 : todo = sp->put(JSDOUBLE_IS_NaN(d)
1586 : ? "0 / 0"
1587 : : (d < 0)
1588 : ? "1 / -0"
1589 9 : : "1 / 0");
1590 9 : *opp = JSOP_DIV;
1591 : } else {
1592 0 : ToCStringBuf cbuf;
1593 0 : s = NumberToCString(sp->context, &cbuf, d);
1594 0 : if (!s) {
1595 0 : JS_ReportOutOfMemory(sp->context);
1596 0 : return -1;
1597 : }
1598 0 : JS_ASSERT(strcmp(s, js_Infinity_str) &&
1599 : (*s != '-' ||
1600 : strcmp(s + 1, js_Infinity_str)) &&
1601 0 : strcmp(s, js_NaN_str));
1602 0 : todo = Sprint(sp, s);
1603 : }
1604 9 : return todo;
1605 : }
1606 :
1607 : static jsbytecode *
1608 : Decompile(SprintStack *ss, jsbytecode *pc, int nb);
1609 :
1610 : static JSBool
1611 117 : DecompileSwitch(SprintStack *ss, TableEntry *table, unsigned tableLength,
1612 : jsbytecode *pc, ptrdiff_t switchLength,
1613 : ptrdiff_t defaultOffset, JSBool isCondSwitch)
1614 : {
1615 : JSContext *cx;
1616 : JSPrinter *jp;
1617 : ptrdiff_t off, off2, diff, caseExprOff, todo;
1618 : const char *rval;
1619 : unsigned i;
1620 : jsval key;
1621 : JSString *str;
1622 :
1623 117 : cx = ss->sprinter.context;
1624 117 : jp = ss->printer;
1625 :
1626 : jsbytecode *lvalpc;
1627 117 : const char *lval = PopStr(ss, JSOP_NOP, &lvalpc);
1628 :
1629 : /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1630 117 : if (isCondSwitch)
1631 0 : ss->top++;
1632 :
1633 117 : js_printf(jp, "\tswitch (");
1634 117 : SprintOpcodePermanent(jp, lval, lvalpc);
1635 117 : js_printf(jp, ") {\n");
1636 :
1637 117 : if (tableLength) {
1638 117 : diff = table[0].offset - defaultOffset;
1639 117 : if (diff > 0) {
1640 0 : jp->indent += 2;
1641 0 : js_printf(jp, "\t%s:\n", js_default_str);
1642 0 : jp->indent += 2;
1643 0 : if (!Decompile(ss, pc + defaultOffset, diff))
1644 0 : return JS_FALSE;
1645 0 : jp->indent -= 4;
1646 : }
1647 :
1648 117 : caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
1649 :
1650 270 : for (i = 0; i < tableLength; i++) {
1651 153 : off = table[i].offset;
1652 153 : off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
1653 :
1654 153 : key = table[i].key;
1655 153 : if (isCondSwitch) {
1656 : ptrdiff_t nextCaseExprOff;
1657 :
1658 : /*
1659 : * key encodes the JSOP_CASE bytecode's offset from switchtop.
1660 : * The next case expression follows immediately, unless we are
1661 : * at the last case.
1662 : */
1663 0 : nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
1664 0 : nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
1665 0 : jp->indent += 2;
1666 0 : if (!Decompile(ss, pc + caseExprOff, nextCaseExprOff - caseExprOff))
1667 0 : return JS_FALSE;
1668 0 : caseExprOff = nextCaseExprOff;
1669 :
1670 : /* Balance the stack as if this JSOP_CASE matched. */
1671 0 : --ss->top;
1672 : } else {
1673 : /*
1674 : * key comes from an atom, not the decompiler, so we need to
1675 : * quote it if it's a string literal. But if table[i].label
1676 : * is non-null, key was constant-propagated and label is the
1677 : * name of the const we should show as the case label. We set
1678 : * key to undefined so this identifier is escaped, if required
1679 : * by non-ASCII characters, but not quoted, by QuoteString.
1680 : */
1681 153 : todo = -1;
1682 153 : if (table[i].label) {
1683 0 : str = table[i].label;
1684 0 : key = JSVAL_VOID;
1685 153 : } else if (JSVAL_IS_DOUBLE(key)) {
1686 : JSOp junk;
1687 :
1688 0 : todo = SprintDoubleValue(&ss->sprinter, key, &junk);
1689 0 : if (todo < 0)
1690 0 : return JS_FALSE;
1691 0 : str = NULL;
1692 : } else {
1693 153 : str = ToString(cx, key);
1694 153 : if (!str)
1695 0 : return JS_FALSE;
1696 : }
1697 153 : if (todo >= 0) {
1698 0 : rval = ss->sprinter.stringAt(todo);
1699 : } else {
1700 : rval = QuoteString(&ss->sprinter, str, (jschar)
1701 153 : (JSVAL_IS_STRING(key) ? '"' : 0));
1702 153 : if (!rval)
1703 0 : return JS_FALSE;
1704 : }
1705 153 : ss->sprinter.setOffset(rval);
1706 153 : jp->indent += 2;
1707 153 : js_printf(jp, "\tcase %s:\n", rval);
1708 : }
1709 :
1710 153 : jp->indent += 2;
1711 153 : if (off <= defaultOffset && defaultOffset < off2) {
1712 81 : diff = defaultOffset - off;
1713 81 : if (diff != 0) {
1714 54 : if (!Decompile(ss, pc + off, diff))
1715 0 : return JS_FALSE;
1716 54 : off = defaultOffset;
1717 : }
1718 81 : jp->indent -= 2;
1719 81 : js_printf(jp, "\t%s:\n", js_default_str);
1720 81 : jp->indent += 2;
1721 : }
1722 153 : if (!Decompile(ss, pc + off, off2 - off))
1723 0 : return JS_FALSE;
1724 153 : jp->indent -= 4;
1725 :
1726 : /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1727 153 : if (isCondSwitch)
1728 0 : ++ss->top;
1729 : }
1730 : }
1731 :
1732 117 : if (defaultOffset == switchLength) {
1733 36 : jp->indent += 2;
1734 36 : js_printf(jp, "\t%s:;\n", js_default_str);
1735 36 : jp->indent -= 2;
1736 : }
1737 117 : js_printf(jp, "\t}\n");
1738 :
1739 : /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1740 117 : if (isCondSwitch)
1741 0 : --ss->top;
1742 117 : return JS_TRUE;
1743 : }
1744 :
1745 : #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1746 : JS_BEGIN_MACRO \
1747 : JS_ASSERT(expr); \
1748 : if (!(expr)) { BAD_EXIT; } \
1749 : JS_END_MACRO
1750 :
1751 : #define LOCAL_ASSERT_RV(expr, rv) \
1752 : LOCAL_ASSERT_CUSTOM(expr, return (rv))
1753 :
1754 : static JSAtom *
1755 26944 : GetArgOrVarAtom(JSPrinter *jp, unsigned slot)
1756 : {
1757 26944 : LOCAL_ASSERT_RV(jp->fun, NULL);
1758 26944 : LOCAL_ASSERT_RV(slot < jp->fun->script()->bindings.countLocalNames(), NULL);
1759 26944 : JSAtom *name = (*jp->localNames)[slot];
1760 : #if !JS_HAS_DESTRUCTURING
1761 : LOCAL_ASSERT_RV(name, NULL);
1762 : #endif
1763 26944 : return name;
1764 : }
1765 :
1766 : #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1767 :
1768 : static const char *
1769 205 : GetLocalInSlot(SprintStack *ss, int i, int slot, JSObject *obj)
1770 : {
1771 254 : for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
1772 236 : const Shape &shape = r.front();
1773 :
1774 236 : if (shape.shortid() == slot) {
1775 : /* Ignore the empty destructuring dummy. */
1776 205 : if (!JSID_IS_ATOM(shape.propid()))
1777 18 : continue;
1778 :
1779 187 : JSAtom *atom = JSID_TO_ATOM(shape.propid());
1780 187 : const char *rval = QuoteString(&ss->sprinter, atom, 0);
1781 187 : if (!rval)
1782 0 : return NULL;
1783 :
1784 187 : ss->sprinter.setOffset(rval);
1785 187 : return rval;
1786 : }
1787 : }
1788 :
1789 18 : return GetStr(ss, i);
1790 : }
1791 :
1792 : const char *
1793 8125 : GetLocal(SprintStack *ss, int i)
1794 : {
1795 8125 : ptrdiff_t off = ss->offsets[i];
1796 8125 : if (off >= 0)
1797 7920 : return ss->sprinter.stringAt(off);
1798 :
1799 : /*
1800 : * We must be called from js_DecompileValueGenerator (via Decompile) when
1801 : * dereferencing a local that's undefined or null. Search script->objects
1802 : * for the block containing this local by its stack index, i.
1803 : *
1804 : * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1805 : * no such local. This could mean no blocks (no script objects at all, or
1806 : * none of the script's object literals are blocks), or the stack slot i is
1807 : * not in a block. In either case, return GetStr(ss, i).
1808 : */
1809 205 : JSScript *script = ss->printer->script;
1810 205 : if (!JSScript::isValidOffset(script->objectsOffset))
1811 0 : return GetStr(ss, i);
1812 :
1813 : // In case of a let variable, the stack points to a JSOP_ENTERBLOCK opcode.
1814 : // Get the object number from the block instead of iterating all objects and
1815 : // hoping the right object is found.
1816 205 : if (off <= -2 && ss->printer->pcstack) {
1817 196 : jsbytecode *pc = ss->printer->pcstack[-2 - off];
1818 :
1819 196 : JS_ASSERT(ss->printer->script->code <= pc);
1820 196 : JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length));
1821 :
1822 196 : if (JSOP_ENTERBLOCK == (JSOp)*pc) {
1823 39 : JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
1824 :
1825 39 : if (obj->isBlock()) {
1826 39 : uint32_t depth = obj->asBlock().stackDepth();
1827 39 : uint32_t count = obj->asBlock().slotCount();
1828 39 : if (uint32_t(i - depth) < uint32_t(count))
1829 39 : return GetLocalInSlot(ss, i, int(i - depth), obj);
1830 : }
1831 : }
1832 : }
1833 :
1834 : // Iterate over all objects.
1835 184 : for (jsatomid j = 0, n = script->objects()->length; j != n; j++) {
1836 184 : JSObject *obj = script->getObject(j);
1837 :
1838 184 : if (obj->isBlock()) {
1839 166 : uint32_t depth = obj->asBlock().stackDepth();
1840 166 : uint32_t count = obj->asBlock().slotCount();
1841 166 : if (uint32_t(i - depth) < uint32_t(count))
1842 166 : return GetLocalInSlot(ss, i, int(i - depth), obj);
1843 : }
1844 : }
1845 :
1846 0 : return GetStr(ss, i);
1847 : }
1848 :
1849 : #undef LOCAL_ASSERT
1850 :
1851 : static JSBool
1852 15488 : IsVarSlot(JSPrinter *jp, jsbytecode *pc, int *indexp)
1853 : {
1854 : unsigned slot;
1855 :
1856 15488 : slot = GET_SLOTNO(pc);
1857 15488 : if (slot < jp->script->nfixed) {
1858 : /* The slot refers to a variable with name stored in jp->localNames. */
1859 6877 : *indexp = jp->fun->nargs + slot;
1860 6877 : return JS_TRUE;
1861 : }
1862 :
1863 : /* We have a local which index is relative to the stack base. */
1864 8611 : slot -= jp->script->nfixed;
1865 8611 : JS_ASSERT(slot < StackDepth(jp->script));
1866 8611 : *indexp = slot;
1867 8611 : return JS_FALSE;
1868 : }
1869 :
1870 : #define LOAD_ATOM(PCOFF) (atom = (jp->script->getAtom(GET_UINT32_INDEX((pc) + PCOFF))))
1871 :
1872 : typedef Vector<JSAtom *, 8> AtomVector;
1873 : typedef AtomVector::Range AtomRange;
1874 :
1875 : #if JS_HAS_DESTRUCTURING
1876 :
1877 : #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1878 : #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1879 :
1880 : static jsbytecode *
1881 : DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1882 : AtomRange *letNames = NULL);
1883 :
1884 : /*
1885 : * Decompile a single element of a compound {}/[] destructuring lhs, sprinting
1886 : * the result in-place (without pushing/popping the stack) and advancing the pc
1887 : * to either the next element or the final pop.
1888 : *
1889 : * For normal (SRC_DESTRUCT) destructuring, the names of assigned/initialized
1890 : * variables are read from their slots. However, for SRC_DESTRUCTLET, the slots
1891 : * have not been pushed yet; the caller must pass the names to use via
1892 : * 'letNames'. Each variable initialized in this destructuring lhs results in
1893 : * popping a name from 'letNames'.
1894 : */
1895 : static jsbytecode *
1896 8631 : DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JSBool *hole,
1897 : AtomRange *letNames = NULL)
1898 : {
1899 : JSPrinter *jp;
1900 : JSOp op;
1901 : const JSCodeSpec *cs;
1902 : unsigned oplen;
1903 : int i;
1904 : const char *lval, *xval;
1905 : JSAtom *atom;
1906 :
1907 8631 : *hole = JS_FALSE;
1908 8631 : jp = ss->printer;
1909 8631 : LOAD_OP_DATA(pc);
1910 :
1911 8631 : switch (op) {
1912 : case JSOP_POP:
1913 1026 : *hole = JS_TRUE;
1914 1026 : if (ss->sprinter.put(", ", 2) < 0)
1915 0 : return NULL;
1916 1026 : break;
1917 :
1918 : case JSOP_PICK:
1919 : /*
1920 : * For 'let ([x, y] = y)', the emitter generates
1921 : *
1922 : * push evaluation of y
1923 : * dup
1924 : * 1 one
1925 : * 2 getelem
1926 : * 3 pick
1927 : * 4 two
1928 : * getelem
1929 : * pick
1930 : * pop
1931 : *
1932 : * Thus 'x' consists of 1 - 3. The caller (DecompileDestructuring or
1933 : * DecompileGroupAssignment) will have taken care of 1 - 2, so pc is
1934 : * now pointing at 3. The pick indicates a primitive let var init so
1935 : * pop a name and advance the pc to 4.
1936 : */
1937 1332 : LOCAL_ASSERT(letNames && !letNames->empty());
1938 1332 : if (!QuoteString(&ss->sprinter, letNames->popCopyFront(), 0))
1939 0 : return NULL;
1940 1332 : break;
1941 :
1942 : case JSOP_DUP:
1943 : {
1944 : /* Compound lhs, e.g., '[x,y]' in 'let [[x,y], z] = a;'. */
1945 4671 : pc = DecompileDestructuring(ss, pc, endpc, letNames);
1946 4671 : if (!pc)
1947 0 : return NULL;
1948 4671 : if (pc == endpc)
1949 0 : return pc;
1950 4671 : LOAD_OP_DATA(pc);
1951 :
1952 : /*
1953 : * By its post-condition, DecompileDestructuring pushed one string
1954 : * containing the whole decompiled lhs. Our post-condition is to sprint
1955 : * in-place so pop/concat this pushed string.
1956 : */
1957 4671 : lval = PopStr(ss, JSOP_NOP);
1958 4671 : if (ss->sprinter.put(lval) < 0)
1959 0 : return NULL;
1960 :
1961 4671 : LOCAL_ASSERT(*pc == JSOP_POP);
1962 :
1963 : /*
1964 : * To put block slots in the right place, the emitter follows a
1965 : * compound lhs with a pick (if at least one slot was pushed). The pick
1966 : * is not part of the compound lhs so DecompileDestructuring did not
1967 : * advance over it but it is part of the lhs so advance over it here.
1968 : */
1969 4671 : jsbytecode *nextpc = pc + JSOP_POP_LENGTH;
1970 4671 : LOCAL_ASSERT(nextpc <= endpc);
1971 4671 : if (letNames && *nextpc == JSOP_PICK) {
1972 567 : LOCAL_ASSERT(nextpc < endpc);
1973 567 : pc = nextpc;
1974 567 : LOAD_OP_DATA(pc);
1975 : }
1976 4671 : break;
1977 : }
1978 :
1979 : case JSOP_SETARG:
1980 : case JSOP_SETLOCAL:
1981 198 : LOCAL_ASSERT(!letNames);
1982 198 : LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
1983 : /* FALL THROUGH */
1984 : case JSOP_SETLOCALPOP:
1985 1575 : LOCAL_ASSERT(!letNames);
1986 1575 : if (op == JSOP_SETARG) {
1987 198 : atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
1988 198 : LOCAL_ASSERT(atom);
1989 198 : if (!QuoteString(&ss->sprinter, atom, 0))
1990 0 : return NULL;
1991 1377 : } else if (IsVarSlot(jp, pc, &i)) {
1992 504 : atom = GetArgOrVarAtom(jp, i);
1993 504 : LOCAL_ASSERT(atom);
1994 504 : if (!QuoteString(&ss->sprinter, atom, 0))
1995 0 : return NULL;
1996 : } else {
1997 873 : lval = GetLocal(ss, i);
1998 873 : if (!lval || ss->sprinter.put(lval) < 0)
1999 0 : return NULL;
2000 : }
2001 1575 : if (op != JSOP_SETLOCALPOP) {
2002 198 : pc += oplen;
2003 198 : if (pc == endpc)
2004 0 : return pc;
2005 198 : LOAD_OP_DATA(pc);
2006 198 : if (op == JSOP_POPN)
2007 0 : return pc;
2008 198 : LOCAL_ASSERT(op == JSOP_POP);
2009 : }
2010 1575 : break;
2011 :
2012 : default: {
2013 27 : LOCAL_ASSERT(!letNames);
2014 : /*
2015 : * We may need to auto-parenthesize the left-most value decompiled
2016 : * here, so add back PAREN_SLOP temporarily. Then decompile until the
2017 : * opcode that would reduce the stack depth to (ss->top-1), which we
2018 : * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
2019 : * the nb parameter.
2020 : */
2021 27 : ptrdiff_t todo = ss->sprinter.getOffset();
2022 27 : ss->sprinter.reserve(PAREN_SLOP);
2023 27 : pc = Decompile(ss, pc, -((int)ss->top));
2024 27 : if (!pc)
2025 0 : return NULL;
2026 27 : if (pc == endpc)
2027 0 : return pc;
2028 27 : LOAD_OP_DATA(pc);
2029 27 : LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
2030 27 : xval = PopStr(ss, JSOP_NOP);
2031 27 : lval = PopStr(ss, JSOP_GETPROP);
2032 27 : ss->sprinter.setOffset(todo);
2033 27 : if (*lval == '\0') {
2034 : /* lval is from JSOP_BINDNAME, so just print xval. */
2035 27 : todo = ss->sprinter.put(xval);
2036 0 : } else if (*xval == '\0') {
2037 : /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
2038 0 : todo = ss->sprinter.put(lval);
2039 : } else {
2040 : todo = Sprint(&ss->sprinter,
2041 0 : (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
2042 : ? "%s.%s"
2043 : : "%s[%s]",
2044 0 : lval, xval);
2045 : }
2046 27 : if (todo < 0)
2047 0 : return NULL;
2048 27 : break;
2049 : }
2050 : }
2051 :
2052 8631 : LOCAL_ASSERT(pc < endpc);
2053 8631 : pc += oplen;
2054 8631 : return pc;
2055 : }
2056 :
2057 : /*
2058 : * Decompile a destructuring lhs object or array initialiser, including nested
2059 : * destructuring initialisers. On return a single string is pushed containing
2060 : * the entire lhs (regardless of how many variables were bound). Thus, the
2061 : * caller must take care of fixing up the decompiler stack.
2062 : *
2063 : * See DecompileDestructuringLHS for description of 'letNames'.
2064 : */
2065 : static jsbytecode *
2066 7740 : DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
2067 : AtomRange *letNames)
2068 : {
2069 7740 : LOCAL_ASSERT(*pc == JSOP_DUP);
2070 7740 : pc += JSOP_DUP_LENGTH;
2071 :
2072 7740 : JSContext *cx = ss->sprinter.context;
2073 7740 : JSPrinter *jp = ss->printer;
2074 7740 : jsbytecode *startpc = pc;
2075 :
2076 : /*
2077 : * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
2078 : * chars so the destructuring decompilation accumulates contiguously in
2079 : * ss->sprinter starting with "[".
2080 : */
2081 7740 : ptrdiff_t head = ss->sprinter.put("[", 1);
2082 7740 : if (head < 0 || !PushOff(ss, head, JSOP_NOP))
2083 0 : return NULL;
2084 7740 : ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
2085 7740 : LOCAL_ASSERT(head == ss->sprinter.getOffset() - 1);
2086 7740 : LOCAL_ASSERT(ss->sprinter[head] == '[');
2087 :
2088 7740 : int lasti = -1;
2089 :
2090 17784 : while (pc < endpc) {
2091 : #if JS_HAS_DESTRUCTURING_SHORTHAND
2092 10044 : ptrdiff_t nameoff = -1;
2093 : #endif
2094 :
2095 : const JSCodeSpec *cs;
2096 : unsigned oplen;
2097 : JSOp op;
2098 10044 : LOAD_OP_DATA(pc);
2099 :
2100 : int i;
2101 : double d;
2102 10044 : switch (op) {
2103 : case JSOP_POP:
2104 : /* Empty destructuring lhs. */
2105 2223 : LOCAL_ASSERT(startpc == pc);
2106 2223 : pc += oplen;
2107 2223 : goto out;
2108 :
2109 : /* Handle the optimized number-pushing opcodes. */
2110 4284 : case JSOP_ZERO: d = i = 0; goto do_getelem;
2111 1359 : case JSOP_ONE: d = i = 1; goto do_getelem;
2112 0 : case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
2113 0 : case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
2114 540 : case JSOP_INT8: d = i = GET_INT8(pc); goto do_getelem;
2115 0 : case JSOP_INT32: d = i = GET_INT32(pc); goto do_getelem;
2116 :
2117 : case JSOP_DOUBLE:
2118 0 : d = jp->script->getConst(GET_UINT32_INDEX(pc)).toDouble();
2119 0 : LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
2120 0 : i = (int)d;
2121 :
2122 : do_getelem:
2123 : {
2124 6183 : jssrcnote *sn = js_GetSrcNote(jp->script, pc);
2125 6183 : pc += oplen;
2126 6183 : if (pc == endpc)
2127 0 : return pc;
2128 6183 : LOAD_OP_DATA(pc);
2129 6183 : LOCAL_ASSERT(op == JSOP_GETELEM);
2130 :
2131 : /* Distinguish object from array by opcode or source note. */
2132 6183 : if (sn && SN_TYPE(sn) == SRC_INITPROP) {
2133 162 : ss->sprinter[head] = '{';
2134 324 : if (Sprint(&ss->sprinter, "%g: ", d) < 0)
2135 0 : return NULL;
2136 : } else {
2137 : /* Sanity check for the gnarly control flow above. */
2138 6021 : LOCAL_ASSERT(i == d);
2139 :
2140 : /* Fill in any holes (holes at the end don't matter). */
2141 12042 : while (++lasti < i) {
2142 0 : if (ss->sprinter.put(", ", 2) < 0)
2143 0 : return NULL;
2144 : }
2145 : }
2146 6183 : break;
2147 : }
2148 :
2149 : case JSOP_GETPROP:
2150 : case JSOP_LENGTH:
2151 : {
2152 : JSAtom *atom;
2153 1638 : LOAD_ATOM(0);
2154 1638 : ss->sprinter[head] = '{';
2155 : #if JS_HAS_DESTRUCTURING_SHORTHAND
2156 1638 : nameoff = ss->sprinter.getOffset();
2157 : #endif
2158 1638 : if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\''))
2159 0 : return NULL;
2160 1638 : if (ss->sprinter.put(": ", 2) < 0)
2161 0 : return NULL;
2162 1638 : break;
2163 : }
2164 :
2165 : default:
2166 0 : LOCAL_ASSERT(0);
2167 : }
2168 :
2169 7821 : pc += oplen;
2170 7821 : if (pc == endpc)
2171 0 : return pc;
2172 :
2173 : /*
2174 : * Decompile the left-hand side expression whose bytecode starts at pc
2175 : * and continues for a bounded number of bytecodes or stack operations
2176 : * (and which in any event stops before endpc).
2177 : */
2178 : JSBool hole;
2179 7821 : pc = DecompileDestructuringLHS(ss, pc, endpc, &hole, letNames);
2180 7821 : if (!pc)
2181 0 : return NULL;
2182 :
2183 : #if JS_HAS_DESTRUCTURING_SHORTHAND
2184 7821 : if (nameoff >= 0) {
2185 : ptrdiff_t offset, initlen;
2186 :
2187 1638 : offset = ss->sprinter.getOffset();
2188 1638 : LOCAL_ASSERT(ss->sprinter[offset] == '\0');
2189 1638 : initlen = offset - nameoff;
2190 1638 : LOCAL_ASSERT(initlen >= 4);
2191 :
2192 : /* Early check to rule out odd "name: lval" length. */
2193 1638 : if (((size_t)initlen & 1) == 0) {
2194 : size_t namelen;
2195 : const char *name;
2196 :
2197 : /*
2198 : * Even "name: lval" string length: check for "x: x" and the
2199 : * like, and apply the shorthand if we can.
2200 : */
2201 774 : namelen = (size_t)(initlen - 2) >> 1;
2202 774 : name = ss->sprinter.stringAt(nameoff);
2203 1332 : if (!strncmp(name + namelen, ": ", 2) &&
2204 558 : !strncmp(name, name + namelen + 2, namelen)) {
2205 171 : offset -= namelen + 2;
2206 171 : ss->sprinter[offset] = '\0';
2207 171 : ss->sprinter.setOffset(offset);
2208 : }
2209 : }
2210 : }
2211 : #endif
2212 :
2213 7821 : if (pc == endpc || *pc != JSOP_DUP)
2214 5382 : break;
2215 :
2216 : /*
2217 : * We should stop if JSOP_DUP is either without notes or its note is
2218 : * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
2219 : * last destructuring reference implementing an op= assignment like in
2220 : * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
2221 : * means another destructuring initialiser abuts this one like in
2222 : * '[a] = [b] = c'.
2223 : */
2224 2439 : jssrcnote *sn = js_GetSrcNote(jp->script, pc);
2225 2439 : if (!sn)
2226 0 : break;
2227 2439 : if (SN_TYPE(sn) != SRC_CONTINUE) {
2228 135 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT || SN_TYPE(sn) == SRC_DESTRUCTLET);
2229 135 : break;
2230 : }
2231 :
2232 2304 : if (!hole && ss->sprinter.put(", ", 2) < 0)
2233 0 : return NULL;
2234 :
2235 2304 : pc += JSOP_DUP_LENGTH;
2236 : }
2237 :
2238 : out:
2239 7740 : const char *lval = ss->sprinter.stringAt(head);
2240 7740 : if (ss->sprinter.put((*lval == '[') ? "]" : "}", 1) < 0)
2241 0 : return NULL;
2242 7740 : return pc;
2243 : }
2244 :
2245 : static jsbytecode *
2246 486 : DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
2247 : jssrcnote *sn, ptrdiff_t *todop)
2248 : {
2249 : JSOp op;
2250 : const JSCodeSpec *cs;
2251 : unsigned oplen, start, end, i;
2252 : ptrdiff_t todo;
2253 : JSBool hole;
2254 : const char *rval;
2255 :
2256 486 : LOAD_OP_DATA(pc);
2257 486 : LOCAL_ASSERT(op == JSOP_GETLOCAL);
2258 :
2259 486 : todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
2260 486 : if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
2261 0 : return NULL;
2262 486 : ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
2263 :
2264 324 : for (;;) {
2265 810 : pc += oplen;
2266 810 : if (pc == endpc)
2267 0 : return pc;
2268 810 : pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
2269 810 : if (!pc)
2270 0 : return NULL;
2271 810 : if (pc == endpc)
2272 0 : return pc;
2273 810 : LOAD_OP_DATA(pc);
2274 810 : if (op != JSOP_GETLOCAL)
2275 : break;
2276 324 : if (!hole && ss->sprinter.put(", ", 2) < 0)
2277 0 : return NULL;
2278 : }
2279 :
2280 486 : LOCAL_ASSERT(op == JSOP_POPN);
2281 486 : if (ss->sprinter.put("] = [", 5) < 0)
2282 0 : return NULL;
2283 :
2284 486 : end = ss->top - 1;
2285 486 : start = end - GET_UINT16(pc);
2286 1296 : for (i = start; i < end; i++) {
2287 810 : rval = GetStr(ss, i);
2288 810 : if (Sprint(&ss->sprinter,
2289 : (i == start) ? "%s" : ", %s",
2290 810 : (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
2291 0 : return NULL;
2292 : }
2293 : }
2294 :
2295 486 : if (ss->sprinter.put("]", 1) < 0)
2296 0 : return NULL;
2297 486 : ss->sprinter.setOffset(ss->offsets[i]);
2298 486 : ss->top = start;
2299 486 : *todop = todo;
2300 486 : return pc;
2301 : }
2302 :
2303 : #undef LOCAL_ASSERT
2304 : #undef LOAD_OP_DATA
2305 :
2306 : #endif /* JS_HAS_DESTRUCTURING */
2307 :
2308 : #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, false)
2309 :
2310 : /*
2311 : * The names of the vars of a let block/expr are stored as the ids of the
2312 : * shapes of the block object. Shapes are stored in a singly-linked list in
2313 : * reverse order of addition. This function takes care of putting the names
2314 : * back in declaration order.
2315 : */
2316 : static bool
2317 8406 : GetBlockNames(JSContext *cx, StaticBlockObject &blockObj, AtomVector *atoms)
2318 : {
2319 8406 : size_t numAtoms = blockObj.slotCount();
2320 8406 : LOCAL_ASSERT(numAtoms > 0);
2321 8406 : if (!atoms->resize(numAtoms))
2322 0 : return false;
2323 :
2324 8406 : unsigned i = numAtoms;
2325 20763 : for (Shape::Range r = blockObj.lastProperty()->all(); !r.empty(); r.popFront()) {
2326 12357 : const Shape &shape = r.front();
2327 12357 : LOCAL_ASSERT(shape.hasShortID());
2328 12357 : --i;
2329 12357 : LOCAL_ASSERT((unsigned)shape.shortid() == i);
2330 12357 : (*atoms)[i] = JSID_IS_INT(shape.propid())
2331 : ? cx->runtime->atomState.emptyAtom
2332 12357 : : JSID_TO_ATOM(shape.propid());
2333 : }
2334 :
2335 8406 : LOCAL_ASSERT(i == 0);
2336 8406 : return true;
2337 : }
2338 :
2339 : static bool
2340 6660 : PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms)
2341 : {
2342 15687 : for (size_t i = 0; i < atoms.length(); i++) {
2343 9027 : const char *name = QuoteString(&ss->sprinter, atoms[i], 0);
2344 9027 : if (!name || !PushOff(ss, ss->sprinter.getOffsetOf(name), JSOP_ENTERBLOCK))
2345 0 : return false;
2346 : }
2347 6660 : return true;
2348 : }
2349 :
2350 : /*
2351 : * In the scope of a let, the variables' (decompiler) stack slots must contain
2352 : * the corresponding variable's name. This function updates the N top slots
2353 : * with the N variable names stored in 'atoms'.
2354 : */
2355 : static bool
2356 4527 : AssignBlockNamesToPushedSlots(JSContext *cx, SprintStack *ss, const AtomVector &atoms)
2357 : {
2358 : /* For simplicity, just pop and push. */
2359 4527 : LOCAL_ASSERT(atoms.length() <= (unsigned)ss->top);
2360 10890 : for (size_t i = 0; i < atoms.length(); ++i)
2361 6363 : PopStr(ss, JSOP_NOP);
2362 4527 : return PushBlockNames(cx, ss, atoms);
2363 : }
2364 :
2365 : static const char SkipString[] = "/*skip*/";
2366 : static const char DestructuredString[] = "/*destructured*/";
2367 19870 : static const unsigned DestructuredStringLength = ArrayLength(DestructuredString) - 1;
2368 :
2369 : static ptrdiff_t
2370 3186 : SprintLetBody(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength,
2371 : const char *headChars)
2372 : {
2373 3186 : if (pc[bodyLength] == JSOP_LEAVEBLOCK) {
2374 1548 : js_printf(jp, "\tlet (%s) {\n", headChars);
2375 1548 : jp->indent += 4;
2376 1548 : if (!Decompile(ss, pc, bodyLength))
2377 0 : return -1;
2378 1548 : jp->indent -= 4;
2379 1548 : js_printf(jp, "\t}\n");
2380 1548 : return -2;
2381 : }
2382 :
2383 1638 : LOCAL_ASSERT_RV(pc[bodyLength] == JSOP_LEAVEBLOCKEXPR, -1);
2384 1638 : if (!Decompile(ss, pc, bodyLength))
2385 0 : return -1;
2386 :
2387 1638 : const char *bodyChars = PopStr(ss, JSOP_SETNAME);
2388 1638 : const char *format = *bodyChars == '{' ? "let (%s) (%s)" : "let (%s) %s";
2389 1638 : return Sprint(&ss->sprinter, format, headChars, bodyChars);
2390 : }
2391 :
2392 : /*
2393 : * Get the token to prefix the '=' in an assignment operation, checking whether
2394 : * the last operation was a getter, setter or compound assignment. For compound
2395 : * assignments, marks parents for the lhs and rhs of the operation in the
2396 : * compound assign. For an assignment such as 'a += b', the lhs will appear
2397 : * twice in the bytecode, in read and write operations. We defer generation of
2398 : * the offsets for the initial arithmetic operation until the entire compound
2399 : * assign has been processed.
2400 : */
2401 : static const char *
2402 875 : GetTokenForAssignment(JSPrinter *jp, jssrcnote *sn, JSOp lastop,
2403 : jsbytecode *pc, jsbytecode *rvalpc,
2404 : jsbytecode **lastlvalpc, jsbytecode **lastrvalpc)
2405 : {
2406 : const char *token;
2407 875 : if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
2408 1280 : if (lastop == JSOP_GETTER) {
2409 0 : token = js_getter_str;
2410 640 : } else if (lastop == JSOP_SETTER) {
2411 0 : token = js_setter_str;
2412 : } else {
2413 640 : token = CodeToken[lastop];
2414 640 : if (*lastlvalpc && *lastrvalpc) {
2415 640 : UpdateDecompiledParent(jp, *lastlvalpc, pc, 0);
2416 640 : UpdateDecompiledParent(jp, *lastrvalpc, rvalpc, 0);
2417 : }
2418 : }
2419 : } else {
2420 235 : token = "";
2421 : }
2422 875 : *lastlvalpc = NULL;
2423 875 : *lastrvalpc = NULL;
2424 875 : return token;
2425 : }
2426 :
2427 : static ptrdiff_t
2428 1035 : SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, const char *initPrefix,
2429 : const char *init, jsbytecode *initpc, jsbytecode **ppc, ptrdiff_t *plen)
2430 : {
2431 1035 : jsbytecode *pc = *ppc;
2432 1035 : jssrcnote *sn = js_GetSrcNote(jp->script, pc);
2433 1035 : JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
2434 :
2435 : /* Print the keyword and the possibly empty init-part. */
2436 1035 : js_printf(jp, "\tfor (%s", initPrefix);
2437 1035 : SprintOpcodePermanent(jp, init, initpc);
2438 1035 : js_printf(jp, ";");
2439 :
2440 : /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2441 1035 : JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_POP);
2442 1035 : pc += JSOP_NOP_LENGTH;
2443 :
2444 : /* Get the cond, next, and loop-closing tail offsets. */
2445 1035 : ptrdiff_t cond = js_GetSrcNoteOffset(sn, 0);
2446 1035 : ptrdiff_t next = js_GetSrcNoteOffset(sn, 1);
2447 1035 : ptrdiff_t tail = js_GetSrcNoteOffset(sn, 2);
2448 :
2449 : /* Find the loop head, skipping over any leading GOTO or NOP. */
2450 1035 : jsbytecode *pc2 = pc;
2451 1035 : if (*pc == JSOP_GOTO || *pc == JSOP_NOP)
2452 738 : pc2 += GetBytecodeLength(pc);
2453 1035 : LOCAL_ASSERT(tail + GET_JUMP_OFFSET(pc + tail) == pc2 - pc);
2454 :
2455 1035 : if (cond != tail) {
2456 : /* Decompile the loop condition. */
2457 684 : if (!Decompile(ss, pc + cond, tail - cond))
2458 0 : return -1;
2459 684 : js_printf(jp, " ");
2460 : jsbytecode *condpc;
2461 684 : const char *cond = PopStr(ss, JSOP_NOP, &condpc);
2462 684 : SprintOpcodePermanent(jp, cond, condpc);
2463 : }
2464 :
2465 : /* Need a semicolon whether or not there was a cond. */
2466 1035 : js_puts(jp, ";");
2467 :
2468 1035 : if (next != cond) {
2469 : /*
2470 : * Decompile the loop updater. It may end in a JSOP_POP
2471 : * that we skip; or in a JSOP_POPN that we do not skip,
2472 : * followed by a JSOP_NOP (skipped as if it's a POP).
2473 : * We cope with the difference between these two cases
2474 : * by checking for stack imbalance and popping if there
2475 : * is an rval.
2476 : */
2477 603 : unsigned saveTop = ss->top;
2478 :
2479 603 : if (!Decompile(ss, pc + next, cond - next - JSOP_POP_LENGTH))
2480 0 : return -1;
2481 603 : LOCAL_ASSERT(ss->top - saveTop <= 1U);
2482 603 : jsbytecode *updatepc = NULL;
2483 : const char *update = (ss->top == saveTop)
2484 0 : ? ss->sprinter.stringEnd()
2485 603 : : PopStr(ss, JSOP_NOP, &updatepc);
2486 603 : js_printf(jp, " ");
2487 603 : SprintOpcodePermanent(jp, update, updatepc);
2488 : }
2489 :
2490 : /* Do the loop body. */
2491 1035 : js_printf(jp, ") {\n");
2492 1035 : jp->indent += 4;
2493 1035 : next -= pc2 - pc;
2494 1035 : if (!Decompile(ss, pc2, next))
2495 0 : return -1;
2496 1035 : jp->indent -= 4;
2497 1035 : js_printf(jp, "\t}\n");
2498 :
2499 : /* Set len so pc skips over the entire loop. */
2500 1035 : *ppc = pc;
2501 1035 : *plen = tail + js_CodeSpec[pc[tail]].length;
2502 1035 : return -2;
2503 : }
2504 :
2505 : #undef LOCAL_ASSERT
2506 :
2507 : static JSBool
2508 11096 : InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, unsigned depth)
2509 : {
2510 11096 : if (!ss->sprinter.init())
2511 0 : return JS_FALSE;
2512 11096 : ss->sprinter.setOffset(PAREN_SLOP);
2513 :
2514 : /* Allocate the parallel (to avoid padding) offset, opcode and bytecode stacks. */
2515 11096 : size_t offsetsz = depth * sizeof(ptrdiff_t);
2516 11096 : size_t opcodesz = depth * sizeof(jsbytecode);
2517 11096 : size_t bytecodesz = depth * sizeof(jsbytecode *);
2518 11096 : void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz + bytecodesz);
2519 11096 : if (!space) {
2520 0 : js_ReportOutOfMemory(cx);
2521 0 : return JS_FALSE;
2522 : }
2523 11096 : ss->offsets = (ptrdiff_t *) space;
2524 11096 : ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
2525 11096 : ss->bytecodes = (jsbytecode **) ((char *)space + offsetsz + opcodesz);
2526 :
2527 11096 : ss->top = ss->inArrayInit = 0;
2528 11096 : ss->inGenExp = JS_FALSE;
2529 11096 : ss->printer = jp;
2530 11096 : return JS_TRUE;
2531 : }
2532 :
2533 : template <typename T>
2534 : void
2535 3636 : Swap(T &a, T &b)
2536 : {
2537 3636 : T tmp = a;
2538 3636 : a = b;
2539 3636 : b = tmp;
2540 3636 : }
2541 :
2542 : /*
2543 : * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
2544 : * the decompiler starts at pc and continues until it reaches an opcode for
2545 : * which decompiling would result in the stack depth equaling -(nb + 1).
2546 : */
2547 : static jsbytecode *
2548 21331 : Decompile(SprintStack *ss, jsbytecode *pc, int nb)
2549 : {
2550 : JSContext *cx;
2551 : JSPrinter *jp, *jp2;
2552 : jsbytecode *startpc, *endpc, *pc2, *done, *lvalpc, *rvalpc, *xvalpc;
2553 : ptrdiff_t tail, todo, len, oplen, cond, next;
2554 : JSOp op, lastop, saveop;
2555 : const JSCodeSpec *cs;
2556 : jssrcnote *sn, *sn2;
2557 : const char *lval, *rval, *xval, *fmt, *token;
2558 : unsigned nuses;
2559 : int i, argc;
2560 : JSAtom *atom;
2561 : JSObject *obj;
2562 21331 : JSFunction *fun = NULL; /* init to shut GCC up */
2563 : JSString *str;
2564 : JSBool ok;
2565 : #if JS_HAS_XML_SUPPORT
2566 : JSBool foreach, inXML, quoteAttr;
2567 : #else
2568 : #define inXML JS_FALSE
2569 : #endif
2570 : jsval val;
2571 :
2572 : static const char exception_cookie[] = "/*EXCEPTION*/";
2573 : static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
2574 : static const char forelem_cookie[] = "/*FORELEM*/";
2575 : static const char with_cookie[] = "/*WITH*/";
2576 : static const char dot_format[] = "%s.%s";
2577 : static const char index_format[] = "%s[%s]";
2578 : static const char predot_format[] = "%s%s.%s";
2579 : static const char postdot_format[] = "%s.%s%s";
2580 : static const char preindex_format[] = "%s%s[%s]";
2581 : static const char postindex_format[] = "%s[%s]%s";
2582 : static const char ss_format[] = "%s%s";
2583 : static const char sss_format[] = "%s%s%s";
2584 :
2585 : /* Argument and variables decompilation uses the following to share code. */
2586 : JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
2587 :
2588 : /*
2589 : * Local macros
2590 : */
2591 : #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
2592 : #define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb)) cleanup
2593 : #define DECOMPILE_CODE(pc,nb) DECOMPILE_CODE_CLEANUP(pc,nb,return NULL)
2594 : #define TOP_STR() GetStr(ss, ss->top - 1)
2595 : #define POP_STR() PopStr(ss, op)
2596 : #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
2597 :
2598 : /*
2599 : * Given an atom already fetched from jp->script's atom map, quote/escape its
2600 : * string appropriately into rval, and select fmt from the quoted and unquoted
2601 : * alternatives.
2602 : */
2603 : #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
2604 : JS_BEGIN_MACRO \
2605 : jschar quote_; \
2606 : if (!IsIdentifier(atom)) { \
2607 : quote_ = '\''; \
2608 : fmt = qfmt; \
2609 : } else { \
2610 : quote_ = 0; \
2611 : fmt = ufmt; \
2612 : } \
2613 : rval = QuoteString(&ss->sprinter, atom, quote_); \
2614 : rval = SprintDupeStr(ss, rval); \
2615 : if (!rval) \
2616 : return NULL; \
2617 : JS_END_MACRO
2618 :
2619 : #define GET_SOURCE_NOTE_ATOM(sn, atom) \
2620 : JS_BEGIN_MACRO \
2621 : jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
2622 : \
2623 : LOCAL_ASSERT(atomIndex_ < jp->script->natoms); \
2624 : (atom) = jp->script->atoms[atomIndex_]; \
2625 : JS_END_MACRO
2626 :
2627 : /*
2628 : * Get atom from jp->script's atom map, quote/escape its string appropriately
2629 : * into rval, and select fmt from the quoted and unquoted alternatives.
2630 : */
2631 : #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
2632 : JS_BEGIN_MACRO \
2633 : LOAD_ATOM(0); \
2634 : GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
2635 : JS_END_MACRO
2636 :
2637 : /*
2638 : * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
2639 : * decompile with the constructor parenthesized, but new x.z should not. The
2640 : * normal rules give x(y).z and x.z identical precedence: both are produced by
2641 : * JSOP_GETPROP.
2642 : *
2643 : * Therefore, we need to know in case JSOP_NEW whether the constructor
2644 : * expression contains any unparenthesized function calls. So when building a
2645 : * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
2646 : * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
2647 : */
2648 : #define PROPAGATE_CALLNESS() \
2649 : JS_BEGIN_MACRO \
2650 : if (ss->opcodes[ss->top - 1] == JSOP_CALL || \
2651 : ss->opcodes[ss->top - 1] == JSOP_EVAL || \
2652 : ss->opcodes[ss->top - 1] == JSOP_FUNCALL || \
2653 : ss->opcodes[ss->top - 1] == JSOP_FUNAPPLY) { \
2654 : saveop = JSOP_CALL; \
2655 : } \
2656 : JS_END_MACRO
2657 :
2658 21331 : jsbytecode *lastlvalpc = NULL, *lastrvalpc = NULL;
2659 :
2660 21331 : cx = ss->sprinter.context;
2661 21331 : JS_CHECK_RECURSION(cx, return NULL);
2662 :
2663 21331 : jp = ss->printer;
2664 21331 : startpc = pc;
2665 21331 : endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
2666 21331 : tail = -1;
2667 21331 : todo = -2; /* NB: different from Sprint() error return. */
2668 21331 : saveop = JSOP_NOP;
2669 21331 : sn = NULL;
2670 21331 : rval = NULL;
2671 21331 : bool forOf = false;
2672 : #if JS_HAS_XML_SUPPORT
2673 21331 : foreach = inXML = quoteAttr = JS_FALSE;
2674 : #endif
2675 :
2676 173554 : while (nb < 0 || pc < endpc) {
2677 : /*
2678 : * Move saveop to lastop so prefixed bytecodes can take special action
2679 : * while sharing maximal code. Set op and saveop to the new bytecode,
2680 : * use op in POP_STR to trigger automatic parenthesization, but push
2681 : * saveop at the bottom of the loop if this op pushes. Thus op may be
2682 : * set to nop or otherwise mutated to suppress auto-parens.
2683 : */
2684 131054 : lastop = saveop;
2685 131054 : op = (JSOp) *pc;
2686 131054 : cs = &js_CodeSpec[op];
2687 131054 : saveop = op;
2688 131054 : len = oplen = cs->length;
2689 131054 : nuses = StackUses(jp->script, pc);
2690 :
2691 : /*
2692 : * Here it is possible that nuses > ss->top when the op has a hidden
2693 : * source note. But when nb < 0 we assume that the caller knows that
2694 : * Decompile would never meet such opcodes.
2695 : */
2696 131054 : if (nb < 0) {
2697 81 : LOCAL_ASSERT(ss->top >= nuses);
2698 81 : unsigned ndefs = StackDefs(jp->script, pc);
2699 81 : if ((unsigned) -(nb + 1) == ss->top - nuses + ndefs)
2700 27 : return pc;
2701 : }
2702 :
2703 : /*
2704 : * Save source literal associated with JS now before the following
2705 : * rewrite changes op. See bug 380197.
2706 : */
2707 131027 : token = CodeToken[op];
2708 :
2709 131027 : if (pc + oplen == jp->dvgfence) {
2710 : /*
2711 : * Rewrite non-get ops to their "get" format if the error is in
2712 : * the bytecode at pc, or if at an inner opcode of a 'fat' outer
2713 : * opcode at pc, so we don't decompile more than the error
2714 : * expression.
2715 : */
2716 2448 : uint32_t format = cs->format;
2717 2448 : bool matchPC = false;
2718 2448 : FrameRegsIter iter(cx);
2719 2448 : if (!iter.done()) {
2720 2448 : jsbytecode *npc = iter.pc();
2721 2448 : if (pc == npc) {
2722 464 : matchPC = true;
2723 1984 : } else if (format & JOF_DECOMPOSE) {
2724 0 : if (unsigned(npc - pc) < GetDecomposeLength(pc, js_CodeSpec[*pc].length))
2725 0 : matchPC = true;
2726 : }
2727 : }
2728 2448 : if ((matchPC || (pc == startpc && nuses != 0)) &&
2729 : format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_VARPROP)) {
2730 239 : uint32_t mode = JOF_MODE(format);
2731 239 : if (mode == JOF_NAME) {
2732 : /*
2733 : * JOF_NAME does not imply JOF_ATOM, so we must check for
2734 : * the QARG and QVAR format types, and translate those to
2735 : * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
2736 : * to JSOP_NAME.
2737 : */
2738 28 : uint32_t type = JOF_TYPE(format);
2739 : op = (type == JOF_QARG)
2740 : ? JSOP_GETARG
2741 : : (type == JOF_LOCAL)
2742 : ? JSOP_GETLOCAL
2743 28 : : JSOP_NAME;
2744 :
2745 28 : JS_ASSERT(js_CodeSpec[op].nuses >= 0);
2746 28 : i = nuses - js_CodeSpec[op].nuses;
2747 94 : while (--i >= 0)
2748 38 : PopOff(ss, JSOP_NOP);
2749 : } else {
2750 : /*
2751 : * We must replace the faulting pc's bytecode with a
2752 : * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2753 : * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2754 : * throw away the assignment op's right-hand operand and
2755 : * decompile it as if it were a GET of its left-hand
2756 : * operand.
2757 : */
2758 211 : if (mode == JOF_PROP) {
2759 : op = (JSOp) ((format & JOF_SET)
2760 : ? JSOP_GETPROP2
2761 18 : : JSOP_GETPROP);
2762 193 : } else if (mode == JOF_ELEM) {
2763 : op = (JSOp) ((format & JOF_SET)
2764 : ? JSOP_GETELEM2
2765 193 : : JSOP_GETELEM);
2766 : } else {
2767 : /*
2768 : * Unknown mode (including mode 0) means that op is
2769 : * uncategorized for our purposes, so we must write
2770 : * per-op special case code here.
2771 : */
2772 0 : switch (op) {
2773 : case JSOP_ENUMELEM:
2774 : case JSOP_ENUMCONSTELEM:
2775 0 : op = JSOP_GETELEM;
2776 0 : break;
2777 : case JSOP_SETXMLNAME:
2778 0 : op = JSOp(JSOP_GETELEM2);
2779 0 : break;
2780 : default:
2781 0 : LOCAL_ASSERT(0);
2782 : }
2783 : }
2784 : }
2785 : }
2786 :
2787 2448 : saveop = op;
2788 2448 : if (op >= JSOP_LIMIT) {
2789 18 : if (op == JSOP_GETPROP2)
2790 18 : saveop = JSOP_GETPROP;
2791 0 : else if (op == JSOP_GETELEM2)
2792 0 : saveop = JSOP_GETELEM;
2793 : }
2794 :
2795 2448 : jp->dvgfence = NULL;
2796 : }
2797 :
2798 131027 : jsbytecode *pushpc = pc;
2799 :
2800 131027 : if (token) {
2801 14989 : switch (nuses) {
2802 : case 2:
2803 4091 : sn = js_GetSrcNote(jp->script, pc);
2804 4091 : if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
2805 : /*
2806 : * Avoid over-parenthesizing y in x op= y based on its
2807 : * expansion: x = x op y (replace y by z = w to see the
2808 : * problem).
2809 : */
2810 640 : op = (JSOp) pc[oplen];
2811 640 : rval = PopStr(ss, op, &lastrvalpc);
2812 640 : (void)PopStr(ss, op, &lastlvalpc);
2813 :
2814 : /* Print only the right operand of the assignment-op. */
2815 640 : todo = ss->sprinter.put(rval);
2816 3451 : } else if (!inXML) {
2817 3451 : rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc);
2818 3451 : lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc);
2819 3451 : todo = ss->sprinter.getOffset();
2820 3451 : SprintOpcode(ss, lval, lvalpc, pc, todo);
2821 3451 : Sprint(&ss->sprinter, " %s ", token);
2822 3451 : SprintOpcode(ss, rval, rvalpc, pc, todo);
2823 : } else {
2824 : /* In XML, just concatenate the two operands. */
2825 0 : LOCAL_ASSERT(op == JSOP_ADD);
2826 0 : rval = POP_STR();
2827 0 : lval = POP_STR();
2828 0 : todo = Sprint(&ss->sprinter, ss_format, lval, rval);
2829 : }
2830 4091 : break;
2831 :
2832 : case 1:
2833 37 : rval = PopStrDupe(ss, op, &rvalpc);
2834 37 : todo = ss->sprinter.put(token);
2835 37 : SprintOpcode(ss, rval, rvalpc, pc, todo);
2836 37 : break;
2837 :
2838 : case 0:
2839 10861 : sn = js_GetSrcNote(jp->script, pc);
2840 10861 : if (sn && SN_TYPE(sn) == SRC_CONTINUE) {
2841 : /* Hoisted let decl (e.g. 'y' in 'let (x) { let y; }'). */
2842 270 : todo = ss->sprinter.put(SkipString);
2843 270 : break;
2844 : }
2845 10591 : todo = ss->sprinter.put(token);
2846 10591 : break;
2847 :
2848 : default:
2849 0 : todo = -2;
2850 0 : break;
2851 : }
2852 : } else {
2853 116038 : switch (op) {
2854 : case JSOP_NOP:
2855 : /*
2856 : * Check for a do-while loop, a for-loop with an empty
2857 : * initializer part, a labeled statement, a function
2858 : * definition, or try/finally.
2859 : */
2860 585 : sn = js_GetSrcNote(jp->script, pc);
2861 585 : todo = -2;
2862 585 : switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2863 : case SRC_WHILE:
2864 : /* First instuction (NOP) contains offset to condition */
2865 0 : ++pc;
2866 : /* Second instruction (TRACE) contains offset to JSOP_IFNE */
2867 0 : sn = js_GetSrcNote(jp->script, pc);
2868 0 : tail = js_GetSrcNoteOffset(sn, 0);
2869 0 : LOCAL_ASSERT(pc[tail] == JSOP_IFNE);
2870 0 : js_printf(jp, "\tdo {\n");
2871 0 : jp->indent += 4;
2872 0 : DECOMPILE_CODE(pc, tail);
2873 0 : jp->indent -= 4;
2874 0 : js_printf(jp, "\t} while (");
2875 0 : rval = PopCondStr(ss, &rvalpc);
2876 0 : SprintOpcodePermanent(jp, rval, rvalpc);
2877 0 : js_printf(jp, ");\n");
2878 0 : pc += tail;
2879 0 : len = js_CodeSpec[*pc].length;
2880 0 : todo = -2;
2881 0 : break;
2882 :
2883 : case SRC_FOR:
2884 : /* for loop with empty initializer. */
2885 45 : todo = SprintNormalFor(cx, jp, ss, "", "", NULL, &pc, &len);
2886 45 : break;
2887 :
2888 : case SRC_ENDBRACE:
2889 414 : jp->indent -= 4;
2890 414 : js_printf(jp, "\t}\n");
2891 414 : break;
2892 :
2893 : case SRC_FUNCDEF:
2894 126 : fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0));
2895 : do_function:
2896 126 : js_puts(jp, "\n");
2897 : jp2 = js_NewPrinter(cx, "nested_function", fun,
2898 : jp->indent, jp->pretty, jp->grouped,
2899 126 : jp->strict);
2900 126 : if (!jp2)
2901 0 : return NULL;
2902 126 : ok = js_DecompileFunction(jp2);
2903 126 : if (ok && !jp2->sprinter.empty())
2904 126 : js_puts(jp, jp2->sprinter.string());
2905 126 : js_DestroyPrinter(jp2);
2906 126 : if (!ok)
2907 0 : return NULL;
2908 126 : js_puts(jp, "\n\n");
2909 126 : break;
2910 :
2911 : case SRC_BRACE:
2912 0 : js_printf(jp, "\t{\n");
2913 0 : jp->indent += 4;
2914 0 : len = js_GetSrcNoteOffset(sn, 0);
2915 0 : DECOMPILE_CODE(pc + oplen, len - oplen);
2916 0 : jp->indent -= 4;
2917 0 : js_printf(jp, "\t}\n");
2918 0 : break;
2919 :
2920 : default:;
2921 : }
2922 585 : break;
2923 :
2924 : case JSOP_LABEL:
2925 54 : sn = js_GetSrcNote(jp->script, pc);
2926 54 : todo = -2;
2927 54 : switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2928 : case SRC_LABEL:
2929 54 : GET_SOURCE_NOTE_ATOM(sn, atom);
2930 54 : jp->indent -= 4;
2931 54 : rval = QuoteString(&ss->sprinter, atom, 0);
2932 54 : if (!rval)
2933 0 : return NULL;
2934 54 : ss->sprinter.setOffset(rval);
2935 54 : js_printf(jp, "\t%s:\n", rval);
2936 54 : jp->indent += 4;
2937 54 : break;
2938 :
2939 : case SRC_LABELBRACE:
2940 0 : GET_SOURCE_NOTE_ATOM(sn, atom);
2941 0 : rval = QuoteString(&ss->sprinter, atom, 0);
2942 0 : if (!rval)
2943 0 : return NULL;
2944 0 : ss->sprinter.setOffset(rval);
2945 0 : js_printf(jp, "\t%s: {\n", rval);
2946 0 : jp->indent += 4;
2947 0 : break;
2948 :
2949 : default:
2950 0 : JS_NOT_REACHED("JSOP_LABEL without source note");
2951 : break;
2952 : }
2953 54 : break;
2954 :
2955 : case JSOP_BINDNAME:
2956 : case JSOP_BINDGNAME:
2957 191 : todo = Sprint(&ss->sprinter, "");
2958 191 : break;
2959 :
2960 : case JSOP_TRY:
2961 414 : js_printf(jp, "\ttry {\n");
2962 414 : jp->indent += 4;
2963 414 : todo = -2;
2964 414 : break;
2965 :
2966 : case JSOP_FINALLY:
2967 0 : jp->indent -= 4;
2968 0 : js_printf(jp, "\t} finally {\n");
2969 0 : jp->indent += 4;
2970 :
2971 : /*
2972 : * We push push the pair of exception/restsub cookies to
2973 : * simulate the effects [gosub] or control transfer during
2974 : * exception capturing on the stack.
2975 : */
2976 0 : todo = Sprint(&ss->sprinter, exception_cookie);
2977 0 : if (todo < 0 || !PushOff(ss, todo, op))
2978 0 : return NULL;
2979 0 : todo = Sprint(&ss->sprinter, retsub_pc_cookie);
2980 0 : break;
2981 :
2982 : case JSOP_RETSUB:
2983 0 : rval = POP_STR();
2984 0 : LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
2985 0 : lval = POP_STR();
2986 0 : LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
2987 0 : todo = -2;
2988 0 : break;
2989 :
2990 : case JSOP_GOSUB:
2991 : /*
2992 : * JSOP_GOSUB has no effect on the decompiler's string stack
2993 : * because the next op in bytecode order finds the stack
2994 : * balanced by a JSOP_RETSUB executed elsewhere.
2995 : */
2996 0 : todo = -2;
2997 0 : break;
2998 :
2999 : case JSOP_POPN:
3000 : {
3001 : unsigned newtop, oldtop;
3002 :
3003 : /*
3004 : * The compiler models operand stack depth and fixes the stack
3005 : * pointer on entry to a catch clause based on its depth model.
3006 : * The decompiler must match the code generator's model, which
3007 : * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
3008 : */
3009 1098 : oldtop = ss->top;
3010 1098 : newtop = oldtop - GET_UINT16(pc);
3011 1098 : LOCAL_ASSERT(newtop <= oldtop);
3012 1098 : todo = -2;
3013 :
3014 1098 : sn = js_GetSrcNote(jp->script, pc);
3015 1098 : if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3016 405 : break;
3017 : #if JS_HAS_DESTRUCTURING
3018 693 : if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
3019 : todo = Sprint(&ss->sprinter, "%s[] = [",
3020 0 : VarPrefix(sn));
3021 0 : if (todo < 0)
3022 0 : return NULL;
3023 0 : for (unsigned i = newtop; i < oldtop; i++) {
3024 0 : rval = ss->sprinter.stringAt(ss->offsets[i]);
3025 0 : if (Sprint(&ss->sprinter, ss_format,
3026 : (i == newtop) ? "" : ", ",
3027 : (i == oldtop - 1 && *rval == '\0')
3028 0 : ? ", " : rval) < 0) {
3029 0 : return NULL;
3030 : }
3031 : }
3032 0 : if (ss->sprinter.put("]", 1) < 0)
3033 0 : return NULL;
3034 :
3035 : /*
3036 : * If this is an empty group assignment, we have no stack
3037 : * budget into which we can push our result string. Adjust
3038 : * ss->sprinter.offset so that our consumer can find the
3039 : * empty group assignment decompilation.
3040 : */
3041 0 : if (newtop == oldtop) {
3042 0 : ss->sprinter.setOffset(todo);
3043 : } else {
3044 : /*
3045 : * Kill newtop before the end_groupassignment: label by
3046 : * retracting/popping early.
3047 : */
3048 0 : LOCAL_ASSERT(newtop < oldtop);
3049 0 : ss->sprinter.setOffset(GetOff(ss, newtop));
3050 0 : ss->top = newtop;
3051 : }
3052 :
3053 : end_groupassignment:
3054 486 : LOCAL_ASSERT(*pc == JSOP_POPN);
3055 :
3056 : /*
3057 : * Thread directly to the next opcode if we can, to handle
3058 : * the special cases of a group assignment in the first or
3059 : * last part of a for(;;) loop head, or in a let block or
3060 : * expression head.
3061 : *
3062 : * NB: todo at this point indexes space in ss->sprinter
3063 : * that is liable to be overwritten. The code below knows
3064 : * exactly how long rval lives, or else copies it down via
3065 : * Sprinter::put.
3066 : */
3067 486 : rval = ss->sprinter.stringAt(todo);
3068 486 : rvalpc = NULL;
3069 486 : todo = -2;
3070 486 : pc2 = pc + oplen;
3071 :
3072 486 : if (*pc2 == JSOP_NOP) {
3073 0 : sn = js_GetSrcNote(jp->script, pc2);
3074 0 : if (sn) {
3075 0 : if (SN_TYPE(sn) == SRC_FOR) {
3076 0 : op = JSOP_NOP;
3077 0 : pc = pc2;
3078 0 : todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len);
3079 0 : break;
3080 : }
3081 : } else {
3082 : /*
3083 : * An unnannotated NOP following a POPN must be the
3084 : * third part of for(;;) loop head. If the POPN's
3085 : * immediate operand is 0, then we may have no slot
3086 : * on the sprint-stack in which to push our result
3087 : * string. In this case the result can be recovered
3088 : * at ss->sprinter.base + ss->sprinter.offset.
3089 : */
3090 0 : if (GET_UINT16(pc) == 0)
3091 0 : break;
3092 0 : todo = ss->sprinter.put(rval);
3093 0 : saveop = JSOP_NOP;
3094 : }
3095 : }
3096 :
3097 : /*
3098 : * If control flow reaches this point with todo still -2,
3099 : * just print rval as an expression statement.
3100 : */
3101 486 : if (todo == -2)
3102 486 : js_printf(jp, "\t%s;\n", rval);
3103 486 : break;
3104 : }
3105 : #endif
3106 693 : if (newtop < oldtop) {
3107 693 : ss->sprinter.setOffset(GetOff(ss, newtop));
3108 693 : ss->top = newtop;
3109 : }
3110 693 : break;
3111 : }
3112 :
3113 : case JSOP_EXCEPTION:
3114 : /* The catch decompiler handles this op itself. */
3115 0 : LOCAL_ASSERT(JS_FALSE);
3116 : break;
3117 :
3118 : case JSOP_POP:
3119 : /*
3120 : * By default, do not automatically parenthesize when popping
3121 : * a stacked expression decompilation. We auto-parenthesize
3122 : * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
3123 : * comma operator.
3124 : */
3125 6905 : op = JSOP_POPV;
3126 : /* FALL THROUGH */
3127 :
3128 : case JSOP_POPV:
3129 6905 : sn = js_GetSrcNote(jp->script, pc);
3130 6905 : switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3131 : case SRC_FOR:
3132 : /* Force parens around 'in' expression at 'for' front. */
3133 450 : if (ss->opcodes[ss->top-1] == JSOP_IN)
3134 0 : op = JSOP_LSH;
3135 450 : rval = PopStr(ss, op, &rvalpc);
3136 450 : todo = SprintNormalFor(cx, jp, ss, "", rval, rvalpc, &pc, &len);
3137 450 : break;
3138 :
3139 : case SRC_PCDELTA:
3140 : /* Comma operator: use JSOP_POP for correct precedence. */
3141 891 : op = JSOP_POP;
3142 :
3143 : /* Pop and save to avoid blowing stack depth budget. */
3144 891 : lval = PopStrDupe(ss, op, &lvalpc);
3145 :
3146 : /*
3147 : * The offset tells distance to the end of the right-hand
3148 : * operand of the comma operator.
3149 : */
3150 891 : pushpc = pc;
3151 891 : done = pc + len;
3152 891 : pc += js_GetSrcNoteOffset(sn, 0);
3153 891 : len = 0;
3154 :
3155 891 : if (!Decompile(ss, done, pc - done))
3156 0 : return NULL;
3157 :
3158 : /* Pop Decompile result and print comma expression. */
3159 891 : rval = PopStrDupe(ss, op, &rvalpc);
3160 891 : todo = ss->sprinter.getOffset();
3161 891 : SprintOpcode(ss, lval, lvalpc, pushpc, todo);
3162 891 : ss->sprinter.put(", ");
3163 891 : SprintOpcode(ss, rval, rvalpc, pushpc, todo);
3164 891 : break;
3165 :
3166 : case SRC_HIDDEN:
3167 : /* Hide this pop, it's from a goto in a with or for/in. */
3168 0 : todo = -2;
3169 0 : break;
3170 :
3171 : case SRC_CONTINUE:
3172 : /* Pop the stack, don't print: end of a for-let-in. */
3173 0 : (void) PopOff(ss, op);
3174 0 : todo = -2;
3175 0 : break;
3176 :
3177 : default:
3178 : {
3179 : /* Turn off parens around a yield statement. */
3180 5564 : if (ss->opcodes[ss->top-1] == JSOP_YIELD)
3181 0 : op = JSOP_NOP;
3182 :
3183 : jsbytecode *rvalpc;
3184 5564 : rval = PopStr(ss, op, &rvalpc);
3185 :
3186 : /*
3187 : * Don't emit decompiler-pushed strings that are not
3188 : * handled by other opcodes. They are pushed onto the
3189 : * stack to help model the interpreter stack and should
3190 : * not appear in the decompiler's output.
3191 : */
3192 5564 : if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
3193 : bool parens =
3194 : *rval == '{' ||
3195 5564 : (strncmp(rval, js_function_str, 8) == 0 &&
3196 11128 : rval[8] == ' ');
3197 5564 : js_printf(jp, parens ? "\t(" : "\t");
3198 5564 : SprintOpcodePermanent(jp, rval, rvalpc);
3199 5564 : js_printf(jp, parens ? ");\n" : ";\n");
3200 : } else {
3201 0 : LOCAL_ASSERT(*rval == '\0' ||
3202 : strcmp(rval, exception_cookie) == 0);
3203 : }
3204 5564 : todo = -2;
3205 5564 : break;
3206 : }
3207 : }
3208 6905 : sn = NULL;
3209 6905 : break;
3210 :
3211 : case JSOP_ENTERWITH:
3212 0 : LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
3213 0 : rval = PopStr(ss, op, &rvalpc);
3214 0 : js_printf(jp, "\twith (");
3215 0 : SprintOpcodePermanent(jp, rval, rvalpc);
3216 0 : js_printf(jp, ") {\n");
3217 0 : jp->indent += 4;
3218 0 : todo = Sprint(&ss->sprinter, with_cookie);
3219 0 : break;
3220 :
3221 : case JSOP_LEAVEWITH:
3222 0 : sn = js_GetSrcNote(jp->script, pc);
3223 0 : todo = -2;
3224 0 : if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3225 0 : break;
3226 0 : rval = POP_STR();
3227 0 : LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
3228 0 : jp->indent -= 4;
3229 0 : js_printf(jp, "\t}\n");
3230 0 : break;
3231 :
3232 : case JSOP_ENTERBLOCK:
3233 : {
3234 2133 : obj = jp->script->getObject(GET_UINT32_INDEX(pc));
3235 4266 : AtomVector atoms(cx);
3236 2133 : StaticBlockObject &blockObj = obj->asStaticBlock();
3237 :
3238 2133 : if (!GetBlockNames(cx, blockObj, &atoms) || !PushBlockNames(cx, ss, atoms))
3239 0 : return NULL;
3240 :
3241 2133 : sn = js_GetSrcNote(jp->script, pc);
3242 2133 : switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3243 : #if JS_HAS_BLOCK_SCOPE
3244 : case SRC_BRACE:
3245 27 : js_printf(jp, "\t{\n");
3246 27 : jp->indent += 4;
3247 27 : len = js_GetSrcNoteOffset(sn, 0);
3248 27 : if (!Decompile(ss, pc + oplen, len - oplen))
3249 0 : return NULL;
3250 27 : jp->indent -= 4;
3251 27 : js_printf(jp, "\t}\n");
3252 27 : break;
3253 : #endif
3254 :
3255 : case SRC_CATCH:
3256 414 : jp->indent -= 4;
3257 414 : js_printf(jp, "\t} catch (");
3258 :
3259 414 : pc2 = pc;
3260 414 : pc += oplen;
3261 414 : LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
3262 414 : pc += JSOP_EXCEPTION_LENGTH;
3263 414 : todo = Sprint(&ss->sprinter, exception_cookie);
3264 414 : if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION))
3265 0 : return NULL;
3266 :
3267 414 : if (*pc == JSOP_DUP) {
3268 0 : sn2 = js_GetSrcNote(jp->script, pc);
3269 0 : if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) {
3270 : /*
3271 : * This is a dup to save the exception for later.
3272 : * It is emitted only when the catch head contains
3273 : * an exception guard.
3274 : */
3275 0 : LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0);
3276 0 : pc += JSOP_DUP_LENGTH;
3277 0 : todo = Sprint(&ss->sprinter, exception_cookie);
3278 0 : if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION))
3279 0 : return NULL;
3280 : }
3281 : }
3282 :
3283 : #if JS_HAS_DESTRUCTURING
3284 414 : if (*pc == JSOP_DUP) {
3285 0 : pc = DecompileDestructuring(ss, pc, endpc);
3286 0 : if (!pc)
3287 0 : return NULL;
3288 0 : LOCAL_ASSERT(*pc == JSOP_POP);
3289 0 : pc += JSOP_POP_LENGTH;
3290 0 : lval = PopStr(ss, JSOP_NOP);
3291 0 : js_puts(jp, lval);
3292 : } else {
3293 : #endif
3294 414 : LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP);
3295 414 : pc += JSOP_SETLOCALPOP_LENGTH;
3296 414 : LOCAL_ASSERT(blockObj.slotCount() >= 1);
3297 414 : if (!QuoteString(&jp->sprinter, atoms[0], 0))
3298 0 : return NULL;
3299 : #if JS_HAS_DESTRUCTURING
3300 : }
3301 : #endif
3302 :
3303 : /*
3304 : * Pop the exception_cookie (or its dup in the case of a
3305 : * guarded catch head) off the stack now.
3306 : */
3307 414 : rval = PopStr(ss, JSOP_NOP);
3308 414 : LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
3309 :
3310 414 : len = js_GetSrcNoteOffset(sn, 0);
3311 414 : if (len) {
3312 0 : len -= pc - pc2;
3313 0 : LOCAL_ASSERT(len > 0);
3314 0 : js_printf(jp, " if ");
3315 0 : if (!Decompile(ss, pc, len))
3316 0 : return NULL;
3317 0 : js_printf(jp, "%s", POP_STR());
3318 0 : pc += len;
3319 0 : LOCAL_ASSERT(*pc == JSOP_IFEQ);
3320 0 : pc += js_CodeSpec[*pc].length;
3321 : }
3322 :
3323 414 : js_printf(jp, ") {\n");
3324 414 : jp->indent += 4;
3325 414 : len = 0;
3326 414 : break;
3327 : default:;
3328 : }
3329 :
3330 4266 : todo = -2;
3331 : }
3332 2133 : break;
3333 :
3334 : case JSOP_LEAVEBLOCK:
3335 : case JSOP_LEAVEBLOCKEXPR:
3336 : {
3337 : unsigned top, depth;
3338 :
3339 9621 : sn = js_GetSrcNote(jp->script, pc);
3340 9621 : todo = -2;
3341 9621 : if (op == JSOP_LEAVEBLOCKEXPR) {
3342 1638 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
3343 1638 : rval = POP_STR();
3344 7983 : } else if (sn) {
3345 4068 : LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
3346 4068 : if (SN_TYPE(sn) == SRC_HIDDEN)
3347 3654 : break;
3348 :
3349 : /*
3350 : * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
3351 : * offset does not equal the model stack depth, there must
3352 : * be a copy of the exception value on the stack due to a
3353 : * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
3354 : * case code).
3355 : */
3356 414 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
3357 414 : if ((unsigned)js_GetSrcNoteOffset(sn, 0) != ss->top) {
3358 0 : LOCAL_ASSERT((unsigned)js_GetSrcNoteOffset(sn, 0)
3359 : == ss->top - 1);
3360 0 : rval = POP_STR();
3361 0 : LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
3362 : }
3363 : }
3364 5967 : top = ss->top;
3365 5967 : depth = GET_UINT16(pc);
3366 5967 : LOCAL_ASSERT(top >= depth);
3367 5967 : top -= depth;
3368 5967 : ss->top = top;
3369 5967 : ss->sprinter.setOffset(GetOff(ss, top));
3370 5967 : if (op == JSOP_LEAVEBLOCKEXPR)
3371 1638 : todo = ss->sprinter.put(rval);
3372 5967 : break;
3373 : }
3374 :
3375 : case JSOP_ENTERLET0:
3376 : {
3377 3726 : obj = jp->script->getObject(GET_UINT32_INDEX(pc));
3378 3726 : StaticBlockObject &blockObj = obj->asStaticBlock();
3379 :
3380 7452 : AtomVector atoms(cx);
3381 3726 : if (!GetBlockNames(cx, blockObj, &atoms))
3382 0 : return NULL;
3383 :
3384 3726 : sn = js_GetSrcNote(jp->script, pc);
3385 3726 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_DECL);
3386 3726 : ptrdiff_t letData = js_GetSrcNoteOffset(sn, 0);
3387 3726 : bool groupAssign = LetDataToGroupAssign(letData);
3388 3726 : unsigned letDepth = blockObj.stackDepth();
3389 3726 : LOCAL_ASSERT(letDepth == (unsigned)ss->top - blockObj.slotCount());
3390 3726 : LOCAL_ASSERT(atoms.length() == blockObj.slotCount());
3391 :
3392 : /*
3393 : * Build the list of decompiled rhs expressions. Do this before
3394 : * sprinting the let-head since GetStr can inject stuff on top
3395 : * of the stack (in case js_DecompileValueGenerator).
3396 : */
3397 7452 : Vector<const char *> rhsExprs(cx);
3398 3726 : if (!rhsExprs.resize(atoms.length()))
3399 0 : return NULL;
3400 9117 : for (size_t i = 0; i < rhsExprs.length(); ++i)
3401 5391 : rhsExprs[i] = SprintDupeStr(ss, GetStr(ss, letDepth + i));
3402 :
3403 : /* Build the let head starting at headBegin. */
3404 3726 : ptrdiff_t headBegin = ss->sprinter.getOffset();
3405 :
3406 : /*
3407 : * For group assignment, prepend the '[lhs-vars] = [' here,
3408 : * append rhsExprs in the next loop and append ']' after.
3409 : */
3410 3726 : if (groupAssign) {
3411 270 : if (Sprint(&ss->sprinter, "[") < 0)
3412 0 : return NULL;
3413 756 : for (size_t i = 0; i < atoms.length(); ++i) {
3414 486 : if (i && Sprint(&ss->sprinter, ", ") < 0)
3415 0 : return NULL;
3416 486 : if (!QuoteString(&ss->sprinter, atoms[i], 0))
3417 0 : return NULL;
3418 : }
3419 270 : if (Sprint(&ss->sprinter, "] = [") < 0)
3420 0 : return NULL;
3421 : }
3422 :
3423 9117 : for (size_t i = 0; i < atoms.length(); ++i) {
3424 5391 : const char *rhs = rhsExprs[i];
3425 5391 : if (!strcmp(rhs, SkipString))
3426 855 : continue;
3427 :
3428 4536 : if (i && Sprint(&ss->sprinter, ", ") < 0)
3429 0 : return NULL;
3430 :
3431 4536 : if (groupAssign) {
3432 486 : if (ss->sprinter.put(rhs) < 0)
3433 0 : return NULL;
3434 4050 : } else if (!strncmp(rhs, DestructuredString, DestructuredStringLength)) {
3435 1746 : if (ss->sprinter.put(rhs + DestructuredStringLength) < 0)
3436 0 : return NULL;
3437 : } else {
3438 2304 : JS_ASSERT(atoms[i] != cx->runtime->atomState.emptyAtom);
3439 2304 : if (!QuoteString(&ss->sprinter, atoms[i], 0))
3440 0 : return NULL;
3441 2304 : if (*rhs) {
3442 2106 : uint8_t prec = js_CodeSpec[ss->opcodes[letDepth + i]].prec;
3443 : const char *fmt = prec && prec < js_CodeSpec[JSOP_SETLOCAL].prec
3444 : ? " = (%s)"
3445 2106 : : " = %s";
3446 2106 : if (Sprint(&ss->sprinter, fmt, rhs) < 0)
3447 0 : return NULL;
3448 : }
3449 : }
3450 : }
3451 :
3452 3726 : if (groupAssign && Sprint(&ss->sprinter, "]") < 0)
3453 0 : return NULL;
3454 :
3455 : /* Clone the let head chars before clobbering the stack. */
3456 7452 : DupBuffer head(cx);
3457 3726 : if (!Dup(ss->sprinter.stringAt(headBegin), &head))
3458 0 : return NULL;
3459 3726 : if (!AssignBlockNamesToPushedSlots(cx, ss, atoms))
3460 0 : return NULL;
3461 :
3462 : /* Detect 'for (let ...)' desugared into 'let (...) {for}'. */
3463 3726 : jsbytecode *nextpc = pc + JSOP_ENTERLET0_LENGTH;
3464 3726 : if (*nextpc == JSOP_NOP) {
3465 540 : jssrcnote *nextsn = js_GetSrcNote(jp->script, nextpc);
3466 540 : if (nextsn && SN_TYPE(nextsn) == SRC_FOR) {
3467 540 : pc = nextpc;
3468 540 : todo = SprintNormalFor(cx, jp, ss, "let ", head.begin(), pc, &pc, &len);
3469 : break;
3470 : }
3471 : }
3472 :
3473 : /* Decompile the body and then complete the let block/expr. */
3474 3186 : len = LetDataToOffset(letData);
3475 3186 : pc = nextpc;
3476 3186 : saveop = (JSOp) pc[len];
3477 3186 : todo = SprintLetBody(cx, jp, ss, pc, len, head.begin());
3478 3726 : break;
3479 : }
3480 :
3481 : /*
3482 : * With 'for (let lhs in rhs)' and 'switch (c) { let-decl }',
3483 : * placeholder slots have already been pushed (by JSOP_UNDEFINED).
3484 : * In both the for-let-in and switch-hoisted-let cases:
3485 : * - there is a non-let slot on top of the stack (hence enterlet1)
3486 : * - there is no further special let-handling required:
3487 : * for-let-in will decompile the let head when it decompiles
3488 : * the loop body prologue; there is no let head to decompile
3489 : * with switch.
3490 : * Hence, the only thing to do is update the let vars' slots with
3491 : * their names, taking care to preserve the iter/condition value
3492 : * on top of the stack.
3493 : */
3494 : case JSOP_ENTERLET1:
3495 : {
3496 801 : obj = jp->script->getObject(GET_UINT32_INDEX(pc));
3497 801 : StaticBlockObject &blockObj = obj->asStaticBlock();
3498 :
3499 1602 : AtomVector atoms(cx);
3500 801 : if (!GetBlockNames(cx, blockObj, &atoms))
3501 0 : return NULL;
3502 :
3503 801 : LOCAL_ASSERT(js_GetSrcNote(jp->script, pc) == NULL);
3504 801 : LOCAL_ASSERT(ss->top - 1 == blockObj.stackDepth() + blockObj.slotCount());
3505 801 : jsbytecode *nextpc = pc + JSOP_ENTERLET1_LENGTH;
3506 801 : if (*nextpc == JSOP_GOTO) {
3507 693 : LOCAL_ASSERT(SN_TYPE(js_GetSrcNote(jp->script, nextpc)) == SRC_FOR_IN);
3508 : } else {
3509 108 : LOCAL_ASSERT(*nextpc == JSOP_CONDSWITCH ||
3510 : *nextpc == JSOP_TABLESWITCH ||
3511 : *nextpc == JSOP_LOOKUPSWITCH);
3512 : }
3513 :
3514 1602 : DupBuffer rhs(cx);
3515 801 : if (!Dup(PopStr(ss, JSOP_NOP), &rhs))
3516 0 : return NULL;
3517 801 : if (!AssignBlockNamesToPushedSlots(cx, ss, atoms))
3518 0 : return NULL;
3519 801 : if (!PushStr(ss, rhs.begin(), op))
3520 0 : return NULL;
3521 801 : todo = -2;
3522 801 : break;
3523 : }
3524 :
3525 : case JSOP_GETFCSLOT:
3526 : case JSOP_CALLFCSLOT:
3527 : {
3528 186 : if (!jp->fun)
3529 0 : jp->fun = jp->script->getCallerFunction();
3530 :
3531 186 : if (!jp->localNames) {
3532 0 : JS_ASSERT(fun == jp->fun);
3533 0 : jp->localNames = cx->new_<Vector<JSAtom *> >(cx);
3534 0 : if (!jp->localNames ||
3535 0 : !jp->fun->script()->bindings.getLocalNameArray(cx, jp->localNames))
3536 : {
3537 0 : return NULL;
3538 : }
3539 : }
3540 :
3541 186 : unsigned index = GET_UINT16(pc);
3542 186 : if (index < jp->fun->script()->bindings.countUpvars()) {
3543 186 : index += jp->fun->script()->bindings.countArgsAndVars();
3544 : } else {
3545 : JSUpvarArray *uva;
3546 : #ifdef DEBUG
3547 : /*
3548 : * We must be in an eval called from jp->fun, where
3549 : * jp->script is the eval-compiled script.
3550 : *
3551 : * However, it's possible that a js_Invoke already
3552 : * pushed a frame trying to call Construct on an
3553 : * object that's not a constructor, causing us to be
3554 : * called with an intervening frame on the stack.
3555 : */
3556 0 : StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
3557 0 : if (fp) {
3558 0 : while (!fp->isEvalFrame())
3559 0 : fp = fp->prev();
3560 0 : JS_ASSERT(fp->script() == jp->script);
3561 0 : JS_ASSERT(fp->prev()->fun() == jp->fun);
3562 0 : JS_ASSERT(jp->fun->isInterpreted());
3563 0 : JS_ASSERT(jp->script != jp->fun->script());
3564 0 : JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
3565 : }
3566 : #endif
3567 0 : uva = jp->script->upvars();
3568 0 : index = uva->vector[index].slot();
3569 : }
3570 186 : atom = GetArgOrVarAtom(jp, index);
3571 186 : goto do_name;
3572 : }
3573 :
3574 : case JSOP_CALLLOCAL:
3575 : case JSOP_GETLOCAL:
3576 9112 : if (IsVarSlot(jp, pc, &i)) {
3577 3498 : atom = GetArgOrVarAtom(jp, i);
3578 3498 : LOCAL_ASSERT(atom);
3579 3498 : goto do_name;
3580 : }
3581 5614 : LOCAL_ASSERT((unsigned)i < ss->top);
3582 5614 : sn = js_GetSrcNote(jp->script, pc);
3583 :
3584 : #if JS_HAS_DESTRUCTURING
3585 5614 : if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
3586 : /*
3587 : * Distinguish a js_DecompileValueGenerator call that
3588 : * targets op alone, from decompilation of a full group
3589 : * assignment sequence, triggered by SRC_GROUPASSIGN
3590 : * annotating the first JSOP_GETLOCAL in the sequence.
3591 : */
3592 486 : if (endpc - pc > JSOP_GETLOCAL_LENGTH || pc > startpc) {
3593 486 : pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
3594 486 : if (!pc)
3595 0 : return NULL;
3596 486 : LOCAL_ASSERT(*pc == JSOP_POPN);
3597 486 : len = oplen = JSOP_POPN_LENGTH;
3598 486 : goto end_groupassignment;
3599 : }
3600 :
3601 : /* Null sn to prevent bogus VarPrefix'ing below. */
3602 0 : sn = NULL;
3603 : }
3604 : #endif
3605 :
3606 5128 : rval = GetLocal(ss, i);
3607 5128 : todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
3608 5128 : break;
3609 :
3610 : case JSOP_SETLOCAL:
3611 : case JSOP_SETLOCALPOP:
3612 4378 : if (IsVarSlot(jp, pc, &i)) {
3613 2551 : atom = GetArgOrVarAtom(jp, i);
3614 2551 : LOCAL_ASSERT(atom);
3615 2551 : goto do_setname;
3616 : }
3617 1827 : lval = GetLocal(ss, i);
3618 1827 : rval = PopStrDupe(ss, op, &rvalpc);
3619 1827 : goto do_setlval;
3620 :
3621 : case JSOP_INCLOCAL:
3622 : case JSOP_DECLOCAL:
3623 522 : if (IsVarSlot(jp, pc, &i)) {
3624 279 : atom = GetArgOrVarAtom(jp, i);
3625 279 : LOCAL_ASSERT(atom);
3626 279 : goto do_incatom;
3627 : }
3628 243 : lval = GetLocal(ss, i);
3629 243 : goto do_inclval;
3630 :
3631 : case JSOP_LOCALINC:
3632 : case JSOP_LOCALDEC:
3633 99 : if (IsVarSlot(jp, pc, &i)) {
3634 45 : atom = GetArgOrVarAtom(jp, i);
3635 45 : LOCAL_ASSERT(atom);
3636 45 : goto do_atominc;
3637 : }
3638 54 : lval = GetLocal(ss, i);
3639 54 : goto do_lvalinc;
3640 :
3641 : case JSOP_RETRVAL:
3642 3573 : todo = -2;
3643 3573 : break;
3644 :
3645 : case JSOP_RETURN:
3646 4078 : LOCAL_ASSERT(jp->fun);
3647 4078 : fun = jp->fun;
3648 4078 : if (fun->flags & JSFUN_EXPR_CLOSURE) {
3649 : /* Turn on parens around comma-expression here. */
3650 27 : op = JSOP_SETNAME;
3651 27 : rval = PopStr(ss, op, &rvalpc);
3652 27 : bool parens = (*rval == '{');
3653 27 : if (parens)
3654 0 : js_printf(jp, "(");
3655 27 : SprintOpcodePermanent(jp, rval, rvalpc);
3656 : js_printf(jp, parens ? ")%s" : "%s",
3657 0 : ((fun->flags & JSFUN_LAMBDA) || !fun->atom)
3658 : ? ""
3659 27 : : ";");
3660 27 : todo = -2;
3661 27 : break;
3662 : }
3663 : /* FALL THROUGH */
3664 :
3665 : case JSOP_SETRVAL:
3666 7624 : rval = PopStr(ss, op, &rvalpc);
3667 7624 : if (*rval != '\0') {
3668 7609 : js_printf(jp, "\t%s ", js_return_str);
3669 7609 : SprintOpcodePermanent(jp, rval, rvalpc);
3670 7609 : js_printf(jp, ";\n");
3671 : } else {
3672 15 : js_printf(jp, "\t%s;\n", js_return_str);
3673 : }
3674 7624 : todo = -2;
3675 7624 : break;
3676 :
3677 : #if JS_HAS_GENERATORS
3678 : case JSOP_YIELD:
3679 : #if JS_HAS_GENERATOR_EXPRS
3680 135 : if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc)))
3681 : #endif
3682 : {
3683 : /* Turn off most parens. */
3684 0 : op = JSOP_SETNAME;
3685 0 : rval = POP_STR();
3686 : todo = (*rval != '\0')
3687 : ? Sprint(&ss->sprinter,
3688 0 : (strncmp(rval, js_yield_str, 5) == 0 &&
3689 0 : (rval[5] == ' ' || rval[5] == '\0'))
3690 : ? "%s (%s)"
3691 : : "%s %s",
3692 0 : js_yield_str, rval)
3693 0 : : ss->sprinter.put(js_yield_str);
3694 0 : break;
3695 : }
3696 :
3697 : #if JS_HAS_GENERATOR_EXPRS
3698 135 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
3699 : /* FALL THROUGH */
3700 : #endif
3701 :
3702 : case JSOP_ARRAYPUSH:
3703 : {
3704 : /* Turn off most parens. */
3705 261 : op = JSOP_SETNAME;
3706 :
3707 : /* Pop the expression being pushed or yielded. */
3708 261 : rval = POP_STR();
3709 :
3710 : /*
3711 : * Skip the for loop head stacked by JSOP_GOTO:SRC_FOR_IN until
3712 : * we hit a block local slot (note empty destructuring patterns
3713 : * result in unit-count blocks).
3714 : */
3715 261 : unsigned pos = ss->top;
3716 783 : while (pos != 0) {
3717 522 : op = (JSOp) ss->opcodes[--pos];
3718 522 : if (op == JSOP_ENTERBLOCK)
3719 261 : break;
3720 : }
3721 261 : JS_ASSERT(op == JSOP_ENTERBLOCK);
3722 :
3723 : /*
3724 : * Here, forpos must index the space before the left-most |for|
3725 : * in the single string of accumulated |for| heads and optional
3726 : * final |if (condition)|.
3727 : */
3728 261 : unsigned forpos = pos + 1;
3729 261 : LOCAL_ASSERT(forpos < ss->top);
3730 :
3731 : /*
3732 : * Now move pos downward over the block's local slots. Even an
3733 : * empty destructuring pattern has one (dummy) local.
3734 : */
3735 657 : while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
3736 270 : if (pos == 0)
3737 135 : break;
3738 135 : --pos;
3739 : }
3740 :
3741 : #if JS_HAS_GENERATOR_EXPRS
3742 261 : if (saveop == JSOP_YIELD) {
3743 : /*
3744 : * Generator expression: decompile just rval followed by
3745 : * the string starting at forpos. Leave the result string
3746 : * in ss->offsets[0] so it can be recovered by our caller
3747 : * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3748 : * top of stack to balance yield, which is an expression
3749 : * (so has neutral stack balance).
3750 : */
3751 135 : LOCAL_ASSERT(pos == 0);
3752 135 : xval = ss->sprinter.stringAt(ss->offsets[forpos]);
3753 135 : ss->sprinter.setOffset(PAREN_SLOP);
3754 135 : todo = Sprint(&ss->sprinter, ss_format, rval, xval);
3755 135 : if (todo < 0)
3756 0 : return NULL;
3757 135 : ss->offsets[0] = todo;
3758 135 : ++ss->top;
3759 135 : return pc;
3760 : }
3761 : #endif /* JS_HAS_GENERATOR_EXPRS */
3762 :
3763 : /*
3764 : * Array comprehension: retract the sprinter to the beginning
3765 : * of the array initialiser and decompile "[<rval> for ...]".
3766 : */
3767 126 : JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
3768 126 : LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
3769 :
3770 126 : ptrdiff_t start = ss->offsets[pos];
3771 126 : LOCAL_ASSERT(ss->sprinter[start] == '[' ||
3772 : ss->sprinter[start] == '#');
3773 126 : LOCAL_ASSERT(forpos < ss->top);
3774 126 : xval = ss->sprinter.stringAt(ss->offsets[forpos]);
3775 126 : lval = ss->sprinter.stringAt(start);
3776 126 : ss->sprinter.setOffset(lval);
3777 :
3778 126 : todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
3779 126 : if (todo < 0)
3780 0 : return NULL;
3781 126 : ss->offsets[pos] = todo;
3782 126 : todo = -2;
3783 126 : break;
3784 : }
3785 : #endif /* JS_HAS_GENERATORS */
3786 :
3787 : case JSOP_THROWING:
3788 0 : todo = -2;
3789 0 : break;
3790 :
3791 : case JSOP_THROW:
3792 117 : sn = js_GetSrcNote(jp->script, pc);
3793 117 : todo = -2;
3794 117 : if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3795 0 : break;
3796 117 : rval = PopStr(ss, op, &rvalpc);
3797 117 : js_printf(jp, "\t%s ", js_throw_str);
3798 117 : SprintOpcodePermanent(jp, rval, rvalpc);
3799 117 : js_printf(jp, ";\n");
3800 117 : break;
3801 :
3802 : case JSOP_ITER:
3803 1062 : forOf = (GET_UINT8(pc) == JSITER_FOR_OF);
3804 1062 : foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
3805 1062 : JSITER_FOREACH;
3806 1062 : todo = -2;
3807 1062 : break;
3808 :
3809 : case JSOP_MOREITER:
3810 0 : JS_NOT_REACHED("JSOP_MOREITER");
3811 : break;
3812 :
3813 : case JSOP_ENDITER:
3814 1494 : sn = js_GetSrcNote(jp->script, pc);
3815 1494 : todo = -2;
3816 1494 : if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3817 432 : break;
3818 1062 : (void) PopOff(ss, op);
3819 1062 : break;
3820 :
3821 : case JSOP_GOTO:
3822 2115 : sn = js_GetSrcNote(jp->script, pc);
3823 2115 : switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3824 : case SRC_FOR_IN:
3825 : /*
3826 : * The bytecode around pc looks like this:
3827 : * <<RHS>>
3828 : * iter
3829 : * pc: goto/gotox C [src_for_in(B, D)]
3830 : * A: <<LHS = iternext>>
3831 : * B: pop [maybe a src_decl_var/let]
3832 : * <<S>>
3833 : * C: moreiter
3834 : * ifne/ifnex A
3835 : * enditer
3836 : * D: ...
3837 : *
3838 : * In an array comprehension or generator expression, we
3839 : * construct the for-head and store it in the slot pushed
3840 : * by JSOP_ITER, then recurse to decompile S. The
3841 : * culminating JSOP_ARRAYPUSH or JSOP_YIELD instruction
3842 : * (which S must contain, by construction) glues all the
3843 : * clauses together.
3844 : *
3845 : * Otherwise this is a for-in statement. We eagerly output
3846 : * the for-head and recurse to decompile the controlled
3847 : * statement S.
3848 : *
3849 : * We never decompile the obligatory JSOP_POP,
3850 : * JSOP_MOREITER or JSOP_IFNE, though we do quick asserts
3851 : * to check that they are there.
3852 : */
3853 1062 : cond = GET_JUMP_OFFSET(pc);
3854 1062 : next = js_GetSrcNoteOffset(sn, 0);
3855 1062 : tail = js_GetSrcNoteOffset(sn, 1);
3856 1062 : JS_ASSERT(pc[next] == JSOP_POP);
3857 1062 : JS_ASSERT(pc[cond] == JSOP_LOOPENTRY);
3858 1062 : cond += JSOP_LOOPENTRY_LENGTH;
3859 1062 : JS_ASSERT(pc[cond] == JSOP_MOREITER);
3860 1062 : DECOMPILE_CODE(pc + oplen, next - oplen);
3861 1062 : lval = POP_STR();
3862 :
3863 : /*
3864 : * This string "<next>" comes from jsopcode.tbl. It stands
3865 : * for the result pushed by JSOP_ITERNEXT.
3866 : */
3867 1062 : JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = <next>") == 0);
3868 1062 : const_cast<char *>(lval)[strlen(lval) - 9] = '\0';
3869 1062 : LOCAL_ASSERT(ss->top >= 1);
3870 :
3871 1062 : if (ss->inArrayInit || ss->inGenExp) {
3872 261 : rval = POP_STR();
3873 261 : if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) {
3874 0 : ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP);
3875 0 : if (Sprint(&ss->sprinter, " %s (%s %s %s)",
3876 : foreach ? js_for_each_str : js_for_str,
3877 : lval,
3878 : forOf ? "of" : "in",
3879 0 : rval) < 0) {
3880 0 : return NULL;
3881 : }
3882 :
3883 : /*
3884 : * Do not AddParenSlop here, as we will push the
3885 : * top-most offset again, which will add paren slop
3886 : * for us. We must push to balance the stack budget
3887 : * when nesting for heads in a comprehension.
3888 : */
3889 0 : todo = ss->offsets[ss->top - 1];
3890 : } else {
3891 : todo = Sprint(&ss->sprinter, " %s (%s %s %s)",
3892 : foreach ? js_for_each_str : js_for_str,
3893 : lval,
3894 : forOf ? "of" : "in",
3895 261 : rval);
3896 : }
3897 261 : if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
3898 0 : return NULL;
3899 261 : DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH);
3900 : } else {
3901 : /*
3902 : * As above, rval or an extension of it must remain
3903 : * stacked during loop body decompilation.
3904 : */
3905 801 : rval = GetStr(ss, ss->top - 1);
3906 801 : xval = VarPrefix(js_GetSrcNote(jp->script, pc + next));
3907 : js_printf(jp, "\t%s (%s%s %s %s) {\n",
3908 : foreach ? js_for_each_str : js_for_str,
3909 : xval,
3910 : lval,
3911 : forOf ? "of" : "in",
3912 801 : rval);
3913 801 : jp->indent += 4;
3914 801 : DECOMPILE_CODE(pc + next + JSOP_POP_LENGTH, cond - next - JSOP_POP_LENGTH);
3915 801 : jp->indent -= 4;
3916 801 : js_printf(jp, "\t}\n");
3917 : }
3918 :
3919 1062 : pc += tail;
3920 1062 : LOCAL_ASSERT(*pc == JSOP_IFNE);
3921 1062 : len = js_CodeSpec[*pc].length;
3922 1062 : break;
3923 :
3924 : case SRC_WHILE:
3925 9 : cond = GET_JUMP_OFFSET(pc);
3926 9 : tail = js_GetSrcNoteOffset(sn, 0);
3927 9 : DECOMPILE_CODE(pc + cond, tail - cond);
3928 9 : js_printf(jp, "\twhile (");
3929 9 : rval = PopCondStr(ss, &rvalpc);
3930 9 : SprintOpcodePermanent(jp, rval, rvalpc);
3931 9 : js_printf(jp, ") {\n");
3932 9 : jp->indent += 4;
3933 9 : DECOMPILE_CODE(pc + oplen, cond - oplen);
3934 9 : jp->indent -= 4;
3935 9 : js_printf(jp, "\t}\n");
3936 9 : pc += tail;
3937 9 : LOCAL_ASSERT(*pc == JSOP_IFNE);
3938 9 : len = js_CodeSpec[*pc].length;
3939 9 : todo = -2;
3940 9 : break;
3941 :
3942 : case SRC_CONT2LABEL:
3943 0 : GET_SOURCE_NOTE_ATOM(sn, atom);
3944 0 : rval = QuoteString(&ss->sprinter, atom, 0);
3945 0 : if (!rval)
3946 0 : return NULL;
3947 0 : ss->sprinter.setOffset(rval);
3948 0 : js_printf(jp, "\tcontinue %s;\n", rval);
3949 0 : break;
3950 :
3951 : case SRC_CONTINUE:
3952 0 : js_printf(jp, "\tcontinue;\n");
3953 0 : break;
3954 :
3955 : case SRC_BREAK2LABEL:
3956 54 : GET_SOURCE_NOTE_ATOM(sn, atom);
3957 54 : rval = QuoteString(&ss->sprinter, atom, 0);
3958 54 : if (!rval)
3959 0 : return NULL;
3960 54 : ss->sprinter.setOffset(rval);
3961 54 : js_printf(jp, "\tbreak %s;\n", rval);
3962 54 : break;
3963 :
3964 : case SRC_HIDDEN:
3965 828 : break;
3966 :
3967 : default:
3968 162 : js_printf(jp, "\tbreak;\n");
3969 162 : break;
3970 : }
3971 2115 : todo = -2;
3972 2115 : break;
3973 :
3974 : case JSOP_IFEQ:
3975 : {
3976 1368 : JSBool elseif = JS_FALSE;
3977 :
3978 : if_again:
3979 1368 : len = GET_JUMP_OFFSET(pc);
3980 1368 : sn = js_GetSrcNote(jp->script, pc);
3981 :
3982 1368 : switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3983 : case SRC_IF:
3984 : case SRC_IF_ELSE:
3985 1349 : rval = PopCondStr(ss, &rvalpc);
3986 1349 : if (ss->inArrayInit || ss->inGenExp) {
3987 9 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
3988 9 : ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
3989 9 : if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
3990 0 : return NULL;
3991 9 : AddParenSlop(ss);
3992 : } else {
3993 1340 : js_printf(jp, elseif ? " if (" : "\tif (");
3994 1340 : SprintOpcodePermanent(jp, rval, rvalpc);
3995 1340 : js_printf(jp, ") {\n");
3996 1340 : jp->indent += 4;
3997 : }
3998 :
3999 1349 : if (SN_TYPE(sn) == SRC_IF) {
4000 1322 : DECOMPILE_CODE(pc + oplen, len - oplen);
4001 : } else {
4002 27 : LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
4003 27 : tail = js_GetSrcNoteOffset(sn, 0);
4004 27 : DECOMPILE_CODE(pc + oplen, tail - oplen);
4005 27 : jp->indent -= 4;
4006 27 : pc += tail;
4007 27 : LOCAL_ASSERT(*pc == JSOP_GOTO);
4008 27 : oplen = js_CodeSpec[*pc].length;
4009 27 : len = GET_JUMP_OFFSET(pc);
4010 27 : js_printf(jp, "\t} else");
4011 :
4012 : /*
4013 : * If the second offset for sn is non-zero, it tells
4014 : * the distance from the goto around the else, to the
4015 : * ifeq for the if inside the else that forms an "if
4016 : * else if" chain. Thus cond spans the condition of
4017 : * the second if, so we simply decompile it and start
4018 : * over at label if_again.
4019 : */
4020 27 : cond = js_GetSrcNoteOffset(sn, 1);
4021 27 : if (cond != 0) {
4022 0 : cond -= tail;
4023 0 : DECOMPILE_CODE(pc + oplen, cond - oplen);
4024 0 : pc += cond;
4025 0 : oplen = js_CodeSpec[*pc].length;
4026 0 : elseif = JS_TRUE;
4027 0 : goto if_again;
4028 : }
4029 :
4030 27 : js_printf(jp, " {\n");
4031 27 : jp->indent += 4;
4032 27 : DECOMPILE_CODE(pc + oplen, len - oplen);
4033 : }
4034 :
4035 1349 : if (!ss->inArrayInit && !ss->inGenExp) {
4036 1340 : jp->indent -= 4;
4037 1340 : js_printf(jp, "\t}\n");
4038 : }
4039 1349 : todo = -2;
4040 1349 : break;
4041 :
4042 : case SRC_COND:
4043 19 : xval = PopStrDupe(ss, op, &xvalpc);
4044 19 : len = js_GetSrcNoteOffset(sn, 0);
4045 19 : DECOMPILE_CODE(pc + oplen, len - oplen);
4046 19 : lval = PopStrDupe(ss, op, &lvalpc);
4047 19 : pushpc = pc;
4048 19 : pc += len;
4049 19 : LOCAL_ASSERT(*pc == JSOP_GOTO);
4050 19 : oplen = js_CodeSpec[*pc].length;
4051 19 : len = GET_JUMP_OFFSET(pc);
4052 19 : DECOMPILE_CODE(pc + oplen, len - oplen);
4053 19 : rval = PopStrDupe(ss, op, &rvalpc);
4054 19 : todo = ss->sprinter.getOffset();
4055 19 : SprintOpcode(ss, xval, xvalpc, pushpc, todo);
4056 19 : ss->sprinter.put(" ? ");
4057 19 : SprintOpcode(ss, lval, lvalpc, pushpc, todo);
4058 19 : ss->sprinter.put(" : ");
4059 19 : SprintOpcode(ss, rval, rvalpc, pushpc, todo);
4060 19 : break;
4061 :
4062 : default:
4063 0 : break;
4064 : }
4065 1368 : break;
4066 : }
4067 :
4068 : case JSOP_IFNE:
4069 0 : LOCAL_ASSERT(0);
4070 : break;
4071 :
4072 : case JSOP_OR:
4073 10 : xval = "||";
4074 :
4075 : do_logical_connective:
4076 : /* Top of stack is the first clause in a disjunction (||). */
4077 37 : lval = PopStrDupe(ss, op, &lvalpc);
4078 37 : done = pc + GET_JUMP_OFFSET(pc);
4079 37 : pushpc = pc;
4080 37 : pc += len;
4081 37 : JS_ASSERT(*pc == JSOP_POP);
4082 37 : pc += JSOP_POP_LENGTH;
4083 37 : len = done - pc;
4084 37 : if (!Decompile(ss, pc, len))
4085 0 : return NULL;
4086 37 : rval = PopStrDupe(ss, op, &rvalpc);
4087 37 : if (!rval)
4088 0 : return NULL;
4089 37 : todo = ss->sprinter.getOffset();
4090 37 : SprintOpcode(ss, lval, lvalpc, pushpc, todo);
4091 93 : if (jp->pretty &&
4092 56 : jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
4093 0 : Sprint(&ss->sprinter, " %s\n", xval);
4094 0 : Sprint(&ss->sprinter, "%*s", jp->indent + 4, "");
4095 : } else {
4096 37 : Sprint(&ss->sprinter, " %s ", xval);
4097 : }
4098 37 : SprintOpcode(ss, rval, rvalpc, pushpc, todo);
4099 37 : break;
4100 :
4101 : case JSOP_AND:
4102 27 : xval = "&&";
4103 27 : goto do_logical_connective;
4104 :
4105 : case JSOP_ENUMELEM:
4106 : case JSOP_ENUMCONSTELEM:
4107 : /*
4108 : * The stack has the object under the (top) index expression.
4109 : * The "rval" property id is underneath those two on the stack.
4110 : * The for loop body net and gross lengths can now be adjusted
4111 : * to account for the length of the indexing expression that
4112 : * came after JSOP_FORELEM and before JSOP_ENUMELEM.
4113 : */
4114 0 : atom = NULL;
4115 0 : op = JSOP_NOP; /* turn off parens around xval */
4116 0 : xval = POP_STR();
4117 0 : op = JSOP_GETELEM; /* lval must have high precedence */
4118 0 : lval = POP_STR();
4119 0 : op = saveop;
4120 0 : rval = POP_STR();
4121 0 : LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
4122 0 : if (*xval == '\0') {
4123 0 : todo = ss->sprinter.put(lval);
4124 : } else {
4125 : todo = Sprint(&ss->sprinter,
4126 : (JOF_OPMODE(lastop) == JOF_XMLNAME)
4127 : ? dot_format
4128 : : index_format,
4129 0 : lval, xval);
4130 : }
4131 0 : break;
4132 :
4133 : case JSOP_GETTER:
4134 : case JSOP_SETTER:
4135 0 : todo = -2;
4136 0 : break;
4137 :
4138 : case JSOP_DUP2:
4139 0 : rval = GetStr(ss, ss->top-2);
4140 0 : todo = ss->sprinter.put(rval);
4141 0 : if (todo < 0 || !PushOff(ss, todo,
4142 0 : (JSOp) ss->opcodes[ss->top-2])) {
4143 0 : return NULL;
4144 : }
4145 : /* FALL THROUGH */
4146 :
4147 : case JSOP_DUP:
4148 : #if JS_HAS_DESTRUCTURING
4149 4344 : sn = js_GetSrcNote(jp->script, pc);
4150 4344 : if (sn) {
4151 3042 : if (SN_TYPE(sn) == SRC_DESTRUCT) {
4152 1296 : pc = DecompileDestructuring(ss, pc, endpc);
4153 1296 : if (!pc)
4154 0 : return NULL;
4155 :
4156 1296 : lval = POP_STR(); /* Pop the decompiler result. */
4157 1296 : rval = POP_STR(); /* Pop the initializer expression. */
4158 :
4159 1296 : if (strcmp(rval, forelem_cookie) == 0) {
4160 : todo = Sprint(&ss->sprinter, ss_format,
4161 0 : VarPrefix(sn), lval);
4162 :
4163 : /* Skip POP so the SRC_FOR_IN code can pop for itself. */
4164 0 : if (*pc == JSOP_POP)
4165 0 : len = JSOP_POP_LENGTH;
4166 : } else {
4167 : todo = Sprint(&ss->sprinter, "%s%s = %s",
4168 1296 : VarPrefix(sn), lval, rval);
4169 : }
4170 :
4171 1296 : op = saveop = JSOP_ENUMELEM;
4172 1296 : len = 0;
4173 : } else {
4174 1746 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCTLET);
4175 :
4176 1746 : ptrdiff_t offsetToLet = js_GetSrcNoteOffset(sn, 0);
4177 1746 : LOCAL_ASSERT(*(pc + offsetToLet) == JSOP_ENTERLET0);
4178 :
4179 1746 : obj = jp->script->getObject(GET_UINT32_INDEX(pc + offsetToLet));
4180 1746 : StaticBlockObject &blockObj = obj->asStaticBlock();
4181 :
4182 1746 : uint32_t blockDepth = blockObj.stackDepth();
4183 1746 : LOCAL_ASSERT(blockDepth < ss->top);
4184 1746 : LOCAL_ASSERT(ss->top <= blockDepth + blockObj.slotCount());
4185 :
4186 3492 : AtomVector atoms(cx);
4187 1746 : if (!GetBlockNames(cx, blockObj, &atoms))
4188 0 : return NULL;
4189 :
4190 : /*
4191 : * Skip any initializers preceding this one. E.g., in
4192 : * let (w=1, x=2, [y,z] = a) { ... }
4193 : * skip 'w' and 'x' for the JSOP_DUP of '[y,z] = a'.
4194 : */
4195 1746 : AtomRange letNames = atoms.all();
4196 1746 : uint32_t curDepth = ss->top - 1 /* initializer */;
4197 2205 : for (uint32_t i = blockDepth; i < curDepth; ++i)
4198 459 : letNames.popFront();
4199 :
4200 : /*
4201 : * Pop and copy the rhs before it gets clobbered.
4202 : * Use JSOP_SETLOCAL's precedence since this is =.
4203 : */
4204 3492 : DupBuffer rhs(cx);
4205 1746 : if (!Dup(PopStr(ss, JSOP_SETLOCAL), &rhs))
4206 0 : return NULL;
4207 :
4208 : /* Destructure, tracking how many vars were bound. */
4209 1746 : size_t remainBefore = letNames.remain();
4210 1746 : pc = DecompileDestructuring(ss, pc, endpc, &letNames);
4211 1746 : if (!pc)
4212 0 : return NULL;
4213 1746 : size_t remainAfter = letNames.remain();
4214 :
4215 : /*
4216 : * Merge the lhs and rhs and prefix with a cookie to
4217 : * tell enterlet0 not to prepend "name = ".
4218 : */
4219 1746 : const char *lhs = PopStr(ss, JSOP_NOP);
4220 : ptrdiff_t off = Sprint(&ss->sprinter, "%s%s = %s",
4221 1746 : DestructuredString, lhs, rhs.begin());
4222 1746 : if (off < 0 || !PushOff(ss, off, JSOP_NOP))
4223 0 : return NULL;
4224 :
4225 : /*
4226 : * Only one slot has been pushed (holding the entire
4227 : * decompiled destructuring expression). However, the
4228 : * abstract depth needs one slot per bound var, so push
4229 : * empty strings for the remainder. We don't have to
4230 : * worry about empty destructuring because the parser
4231 : * ensures that there is always at least one pushed
4232 : * slot for each destructuring lhs.
4233 : */
4234 1746 : LOCAL_ASSERT(remainBefore >= remainAfter);
4235 1746 : LOCAL_ASSERT(remainBefore > remainAfter || remainAfter > 0);
4236 2331 : for (size_t i = remainBefore - 1; i > remainAfter; --i) {
4237 585 : if (!PushStr(ss, SkipString, JSOP_NOP))
4238 0 : return NULL;
4239 : }
4240 :
4241 1746 : LOCAL_ASSERT(*pc == JSOP_POP);
4242 1746 : pc += JSOP_POP_LENGTH;
4243 :
4244 : /* Eat up the JSOP_UNDEFINED following empty destructuring. */
4245 1746 : if (remainBefore == remainAfter) {
4246 999 : LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
4247 999 : pc += JSOP_UNDEFINED_LENGTH;
4248 : }
4249 :
4250 1746 : len = 0;
4251 3492 : todo = -2;
4252 : }
4253 3042 : break;
4254 : }
4255 : #endif
4256 :
4257 1302 : rval = GetStr(ss, ss->top-1);
4258 1302 : saveop = (JSOp) ss->opcodes[ss->top-1];
4259 1302 : todo = ss->sprinter.put(rval);
4260 1302 : break;
4261 :
4262 : case JSOP_SWAP:
4263 1212 : Swap(ss->offsets[ss->top-1], ss->offsets[ss->top-2]);
4264 1212 : Swap(ss->opcodes[ss->top-1], ss->opcodes[ss->top-2]);
4265 1212 : Swap(ss->bytecodes[ss->top-1], ss->bytecodes[ss->top-2]);
4266 1212 : todo = -2;
4267 1212 : break;
4268 :
4269 : case JSOP_SETARG:
4270 117 : atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
4271 117 : LOCAL_ASSERT(atom);
4272 117 : goto do_setname;
4273 :
4274 : case JSOP_SETCONST:
4275 : case JSOP_SETNAME:
4276 : case JSOP_SETGNAME:
4277 164 : LOAD_ATOM(0);
4278 :
4279 : do_setname:
4280 2832 : lval = QuoteString(&ss->sprinter, atom, 0);
4281 2832 : if (!lval)
4282 0 : return NULL;
4283 2832 : rval = PopStrDupe(ss, op, &rvalpc);
4284 2832 : if (op == JSOP_SETNAME || op == JSOP_SETGNAME)
4285 164 : (void) PopOff(ss, op);
4286 :
4287 : do_setlval:
4288 4659 : sn = js_GetSrcNote(jp->script, pc - 1);
4289 4659 : todo = ss->sprinter.getOffset();
4290 4659 : if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
4291 : const char *token =
4292 : GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
4293 568 : &lastlvalpc, &lastrvalpc);
4294 568 : Sprint(&ss->sprinter, "%s %s= ", lval, token);
4295 568 : SprintOpcode(ss, rval, rvalpc, pc, todo);
4296 : } else {
4297 4091 : sn = js_GetSrcNote(jp->script, pc);
4298 4091 : const char *prefix = VarPrefix(sn);
4299 4091 : Sprint(&ss->sprinter, "%s%s = ", prefix, lval);
4300 4091 : SprintOpcode(ss, rval, rvalpc, pc, todo);
4301 : }
4302 4659 : if (op == JSOP_SETLOCALPOP) {
4303 0 : if (!PushOff(ss, todo, saveop))
4304 0 : return NULL;
4305 0 : rval = POP_STR();
4306 0 : LOCAL_ASSERT(*rval != '\0');
4307 0 : js_printf(jp, "\t%s;\n", rval);
4308 0 : todo = -2;
4309 : }
4310 4659 : break;
4311 :
4312 : case JSOP_NEW:
4313 : case JSOP_CALL:
4314 : case JSOP_EVAL:
4315 : case JSOP_FUNCALL:
4316 : case JSOP_FUNAPPLY:
4317 : {
4318 3767 : argc = GET_ARGC(pc);
4319 : const char **argv = (const char **)
4320 3767 : cx->malloc_((size_t)(argc + 1) * sizeof *argv);
4321 3767 : if (!argv)
4322 0 : return NULL;
4323 : jsbytecode **argbytecodes = (jsbytecode **)
4324 3767 : cx->malloc_((size_t)(argc + 1) * sizeof *argbytecodes);
4325 3767 : if (!argbytecodes) {
4326 0 : cx->free_(argv);
4327 0 : return NULL;
4328 : }
4329 :
4330 3767 : op = JSOP_SETNAME;
4331 7605 : for (i = argc; i > 0; i--)
4332 3838 : argv[i] = PopStrDupe(ss, op, &argbytecodes[i]);
4333 :
4334 : /* Skip the JSOP_PUSHOBJ-created empty string. */
4335 3767 : LOCAL_ASSERT(ss->top >= 2);
4336 3767 : (void) PopOff(ss, op);
4337 :
4338 : /*
4339 : * Special case: new (x(y)(z)) must be parenthesized like so.
4340 : * Same for new (x(y).z) -- contrast with new x(y).z.
4341 : * See PROPAGATE_CALLNESS.
4342 : */
4343 3767 : op = (JSOp) ss->opcodes[ss->top - 1];
4344 : argv[0] = PopStrDupe(ss,
4345 : (saveop == JSOP_NEW &&
4346 : (op == JSOP_CALL ||
4347 : op == JSOP_EVAL ||
4348 : op == JSOP_FUNCALL ||
4349 : op == JSOP_FUNAPPLY ||
4350 : op == JSOP_CALLPROP ||
4351 : op == JSOP_CALLELEM))
4352 : ? JSOP_NAME
4353 : : saveop,
4354 3767 : &lvalpc);
4355 3767 : op = saveop;
4356 :
4357 3767 : lval = "(", rval = ")";
4358 3767 : todo = ss->sprinter.getOffset();
4359 3767 : if (op == JSOP_NEW) {
4360 258 : if (argc == 0)
4361 108 : lval = rval = "";
4362 258 : Sprint(&ss->sprinter, "%s ", js_new_str);
4363 : }
4364 3767 : SprintOpcode(ss, argv[0], lvalpc, pc, todo);
4365 3767 : ss->sprinter.put(lval);
4366 :
4367 7605 : for (i = 1; i <= argc; i++) {
4368 3838 : SprintOpcode(ss, argv[i], argbytecodes[i], pc, todo);
4369 3838 : if (i < argc)
4370 592 : ss->sprinter.put(", ");
4371 : }
4372 3767 : ss->sprinter.put(rval);
4373 :
4374 3767 : cx->free_(argv);
4375 3767 : cx->free_(argbytecodes);
4376 :
4377 3767 : break;
4378 : }
4379 :
4380 : case JSOP_SETCALL:
4381 0 : todo = Sprint(&ss->sprinter, "");
4382 0 : break;
4383 :
4384 : case JSOP_DELNAME:
4385 0 : LOAD_ATOM(0);
4386 0 : lval = QuoteString(&ss->sprinter, atom, 0);
4387 0 : if (!lval)
4388 0 : return NULL;
4389 0 : ss->sprinter.setOffset(lval);
4390 : do_delete_lval:
4391 0 : todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
4392 0 : break;
4393 :
4394 : case JSOP_DELPROP:
4395 0 : GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
4396 0 : op = JSOP_GETPROP;
4397 0 : lval = POP_STR();
4398 0 : todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
4399 0 : break;
4400 :
4401 : case JSOP_DELELEM:
4402 0 : op = JSOP_NOP; /* turn off parens */
4403 0 : xval = POP_STR();
4404 0 : op = JSOP_GETPROP;
4405 0 : lval = POP_STR();
4406 0 : if (*xval == '\0')
4407 0 : goto do_delete_lval;
4408 : todo = Sprint(&ss->sprinter,
4409 : (JOF_OPMODE(lastop) == JOF_XMLNAME)
4410 : ? "%s %s.%s"
4411 : : "%s %s[%s]",
4412 0 : js_delete_str, lval, xval);
4413 0 : break;
4414 :
4415 : #if JS_HAS_XML_SUPPORT
4416 : case JSOP_DELDESC:
4417 0 : xval = POP_STR();
4418 0 : op = JSOP_GETPROP;
4419 0 : lval = POP_STR();
4420 : todo = Sprint(&ss->sprinter, "%s %s..%s",
4421 0 : js_delete_str, lval, xval);
4422 0 : break;
4423 : #endif
4424 :
4425 : case JSOP_TYPEOFEXPR:
4426 : case JSOP_TYPEOF:
4427 : case JSOP_VOID:
4428 : {
4429 0 : const char *prefix = (op == JSOP_VOID) ? js_void_str : js_typeof_str;
4430 0 : rval = PopStrDupe(ss, op, &rvalpc);
4431 0 : todo = ss->sprinter.getOffset();
4432 0 : Sprint(&ss->sprinter, "%s ", prefix);
4433 0 : SprintOpcode(ss, rval, rvalpc, pc, todo);
4434 0 : break;
4435 : }
4436 :
4437 : case JSOP_INCARG:
4438 : case JSOP_DECARG:
4439 54 : atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
4440 54 : LOCAL_ASSERT(atom);
4441 54 : goto do_incatom;
4442 :
4443 : case JSOP_INCNAME:
4444 : case JSOP_DECNAME:
4445 : case JSOP_INCGNAME:
4446 : case JSOP_DECGNAME:
4447 4 : LOAD_ATOM(0);
4448 : do_incatom:
4449 337 : lval = QuoteString(&ss->sprinter, atom, 0);
4450 337 : if (!lval)
4451 0 : return NULL;
4452 337 : ss->sprinter.setOffset(lval);
4453 : do_inclval:
4454 : todo = Sprint(&ss->sprinter, ss_format,
4455 580 : js_incop_strs[!(cs->format & JOF_INC)], lval);
4456 580 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
4457 4 : len += GetDecomposeLength(pc, js_CodeSpec[*pc].length);
4458 580 : break;
4459 :
4460 : case JSOP_INCPROP:
4461 : case JSOP_DECPROP:
4462 0 : GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
4463 :
4464 : /*
4465 : * Force precedence below the numeric literal opcodes, so that
4466 : * 42..foo or 10000..toString(16), e.g., decompile with parens
4467 : * around the left-hand side of dot.
4468 : */
4469 0 : op = JSOP_GETPROP;
4470 0 : lval = POP_STR();
4471 : todo = Sprint(&ss->sprinter, fmt,
4472 0 : js_incop_strs[!(cs->format & JOF_INC)],
4473 0 : lval, rval);
4474 0 : len += GetDecomposeLength(pc, JSOP_INCPROP_LENGTH);
4475 0 : break;
4476 :
4477 : case JSOP_INCELEM:
4478 : case JSOP_DECELEM:
4479 0 : op = JSOP_NOP; /* turn off parens */
4480 0 : xval = POP_STR();
4481 0 : op = JSOP_GETELEM;
4482 0 : lval = POP_STR();
4483 0 : if (*xval != '\0') {
4484 : todo = Sprint(&ss->sprinter,
4485 : (JOF_OPMODE(lastop) == JOF_XMLNAME)
4486 : ? predot_format
4487 : : preindex_format,
4488 0 : js_incop_strs[!(cs->format & JOF_INC)],
4489 0 : lval, xval);
4490 : } else {
4491 : todo = Sprint(&ss->sprinter, ss_format,
4492 0 : js_incop_strs[!(cs->format & JOF_INC)], lval);
4493 : }
4494 0 : len += GetDecomposeLength(pc, JSOP_INCELEM_LENGTH);
4495 0 : break;
4496 :
4497 : case JSOP_ARGINC:
4498 : case JSOP_ARGDEC:
4499 0 : atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
4500 0 : LOCAL_ASSERT(atom);
4501 0 : goto do_atominc;
4502 :
4503 : case JSOP_NAMEINC:
4504 : case JSOP_NAMEDEC:
4505 : case JSOP_GNAMEINC:
4506 : case JSOP_GNAMEDEC:
4507 36 : LOAD_ATOM(0);
4508 : do_atominc:
4509 81 : lval = QuoteString(&ss->sprinter, atom, 0);
4510 81 : if (!lval)
4511 0 : return NULL;
4512 81 : ss->sprinter.setOffset(lval);
4513 : do_lvalinc:
4514 : todo = Sprint(&ss->sprinter, ss_format,
4515 135 : lval, js_incop_strs[!(cs->format & JOF_INC)]);
4516 135 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
4517 36 : len += GetDecomposeLength(pc, js_CodeSpec[*pc].length);
4518 135 : break;
4519 :
4520 : case JSOP_PROPINC:
4521 : case JSOP_PROPDEC:
4522 0 : GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
4523 :
4524 : /*
4525 : * Force precedence below the numeric literal opcodes, so that
4526 : * 42..foo or 10000..toString(16), e.g., decompile with parens
4527 : * around the left-hand side of dot.
4528 : */
4529 0 : op = JSOP_GETPROP;
4530 0 : lval = POP_STR();
4531 : todo = Sprint(&ss->sprinter, fmt, lval, rval,
4532 0 : js_incop_strs[!(cs->format & JOF_INC)]);
4533 0 : len += GetDecomposeLength(pc, JSOP_PROPINC_LENGTH);
4534 0 : break;
4535 :
4536 : case JSOP_ELEMINC:
4537 : case JSOP_ELEMDEC:
4538 19 : op = JSOP_NOP; /* turn off parens */
4539 19 : xval = POP_STR();
4540 19 : op = JSOP_GETELEM;
4541 19 : lval = POP_STR();
4542 19 : if (*xval != '\0') {
4543 : todo = Sprint(&ss->sprinter,
4544 : (JOF_OPMODE(lastop) == JOF_XMLNAME)
4545 : ? postdot_format
4546 : : postindex_format,
4547 : lval, xval,
4548 10 : js_incop_strs[!(cs->format & JOF_INC)]);
4549 : } else {
4550 : todo = Sprint(&ss->sprinter, ss_format,
4551 9 : lval, js_incop_strs[!(cs->format & JOF_INC)]);
4552 : }
4553 19 : len += GetDecomposeLength(pc, JSOP_ELEMINC_LENGTH);
4554 19 : break;
4555 :
4556 : case JSOP_GETPROP2:
4557 18 : op = JSOP_GETPROP;
4558 18 : (void) PopOff(ss, lastop);
4559 : /* FALL THROUGH */
4560 :
4561 : case JSOP_CALLPROP:
4562 : case JSOP_GETPROP:
4563 : case JSOP_GETXPROP:
4564 : case JSOP_LENGTH:
4565 2614 : LOAD_ATOM(0);
4566 :
4567 2614 : GET_QUOTE_AND_FMT("[%s]", ".%s", rval);
4568 2614 : PROPAGATE_CALLNESS();
4569 2614 : lval = PopStr(ss, op, &lvalpc);
4570 2614 : todo = ss->sprinter.getOffset();
4571 2614 : SprintOpcode(ss, lval, lvalpc, pc, todo);
4572 2614 : Sprint(&ss->sprinter, fmt, rval);
4573 2614 : break;
4574 :
4575 : case JSOP_SETPROP:
4576 : case JSOP_SETMETHOD:
4577 : {
4578 280 : LOAD_ATOM(0);
4579 280 : GET_QUOTE_AND_FMT("[%s] %s= ", ".%s %s= ", xval);
4580 280 : rval = PopStrDupe(ss, op, &rvalpc);
4581 :
4582 : /*
4583 : * Force precedence below the numeric literal opcodes, so that
4584 : * 42..foo or 10000..toString(16), e.g., decompile with parens
4585 : * around the left-hand side of dot.
4586 : */
4587 280 : op = JSOP_GETPROP;
4588 280 : lval = PopStr(ss, op, &lvalpc);
4589 280 : sn = js_GetSrcNote(jp->script, pc - 1);
4590 : const char *token =
4591 : GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
4592 280 : &lastlvalpc, &lastrvalpc);
4593 280 : todo = ss->sprinter.getOffset();
4594 280 : SprintOpcode(ss, lval, lvalpc, pc, todo);
4595 280 : Sprint(&ss->sprinter, fmt, xval, token);
4596 280 : SprintOpcode(ss, rval, rvalpc, pc, todo);
4597 280 : break;
4598 : }
4599 :
4600 : case JSOP_GETELEM2:
4601 0 : (void) PopOff(ss, lastop);
4602 : /* FALL THROUGH */
4603 : case JSOP_CALLELEM:
4604 : case JSOP_GETELEM:
4605 721 : op = JSOP_NOP; /* turn off parens */
4606 721 : xval = PopStrDupe(ss, op, &xvalpc);
4607 721 : op = saveop;
4608 721 : PROPAGATE_CALLNESS();
4609 721 : lval = PopStr(ss, op, &lvalpc);
4610 721 : todo = ss->sprinter.getOffset();
4611 721 : SprintOpcode(ss, lval, lvalpc, pc, todo);
4612 721 : if (*xval != '\0') {
4613 712 : bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME);
4614 712 : ss->sprinter.put(xml ? "." : "[");
4615 712 : SprintOpcode(ss, xval, xvalpc, pc, todo);
4616 712 : ss->sprinter.put(xml ? "" : "]");
4617 : }
4618 721 : break;
4619 :
4620 : case JSOP_SETELEM:
4621 : {
4622 27 : rval = PopStrDupe(ss, op, &rvalpc);
4623 27 : op = JSOP_NOP; /* turn off parens */
4624 27 : xval = PopStrDupe(ss, op, &xvalpc);
4625 27 : cs = &js_CodeSpec[ss->opcodes[ss->top]];
4626 27 : op = JSOP_GETELEM; /* lval must have high precedence */
4627 27 : lval = PopStr(ss, op, &lvalpc);
4628 27 : op = saveop;
4629 27 : if (*xval == '\0')
4630 0 : goto do_setlval;
4631 27 : sn = js_GetSrcNote(jp->script, pc - 1);
4632 27 : bool xml = (JOF_MODE(cs->format) == JOF_XMLNAME);
4633 : const char *token =
4634 : GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
4635 27 : &lastlvalpc, &lastrvalpc);
4636 27 : todo = ss->sprinter.getOffset();
4637 27 : SprintOpcode(ss, lval, lvalpc, pc, todo);
4638 27 : ss->sprinter.put(xml ? "." : "[");
4639 27 : SprintOpcode(ss, xval, xvalpc, pc, todo);
4640 27 : ss->sprinter.put(xml ? "" : "]");
4641 27 : Sprint(&ss->sprinter, " %s= ", token);
4642 27 : SprintOpcode(ss, rval, rvalpc, pc, todo);
4643 27 : break;
4644 : }
4645 :
4646 : case JSOP_CALLARG:
4647 : case JSOP_GETARG:
4648 12018 : i = GET_ARGNO(pc);
4649 12018 : atom = GetArgOrVarAtom(jp, i);
4650 : #if JS_HAS_DESTRUCTURING
4651 12018 : if (!atom) {
4652 0 : todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
4653 0 : break;
4654 : }
4655 : #else
4656 : LOCAL_ASSERT(atom);
4657 : #endif
4658 12018 : goto do_name;
4659 :
4660 : case JSOP_CALLNAME:
4661 : case JSOP_NAME:
4662 : case JSOP_GETGNAME:
4663 : case JSOP_CALLGNAME:
4664 4902 : LOAD_ATOM(0);
4665 : do_name:
4666 20631 : lval = "";
4667 : #if JS_HAS_XML_SUPPORT
4668 : do_qname:
4669 : #endif
4670 20640 : sn = js_GetSrcNote(jp->script, pc);
4671 20640 : rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : 0);
4672 20640 : if (!rval)
4673 0 : return NULL;
4674 20640 : ss->sprinter.setOffset(rval);
4675 : todo = Sprint(&ss->sprinter, sss_format,
4676 20640 : VarPrefix(sn), lval, rval);
4677 20640 : break;
4678 :
4679 : case JSOP_UINT16:
4680 34 : i = (int) GET_UINT16(pc);
4681 34 : goto do_sprint_int;
4682 :
4683 : case JSOP_UINT24:
4684 0 : i = (int) GET_UINT24(pc);
4685 0 : goto do_sprint_int;
4686 :
4687 : case JSOP_INT8:
4688 1370 : i = GET_INT8(pc);
4689 1370 : goto do_sprint_int;
4690 :
4691 : case JSOP_INT32:
4692 0 : i = GET_INT32(pc);
4693 : do_sprint_int:
4694 1404 : todo = Sprint(&ss->sprinter, "%d", i);
4695 1404 : break;
4696 :
4697 : case JSOP_DOUBLE:
4698 : {
4699 9 : val = jp->script->getConst(GET_UINT32_INDEX(pc));
4700 9 : todo = SprintDoubleValue(&ss->sprinter, val, &saveop);
4701 9 : break;
4702 : }
4703 :
4704 : case JSOP_STRING:
4705 3707 : LOAD_ATOM(0);
4706 3707 : rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : '"');
4707 3707 : if (!rval)
4708 0 : return NULL;
4709 3707 : todo = ss->sprinter.getOffsetOf(rval);
4710 3707 : break;
4711 :
4712 : case JSOP_LAMBDA:
4713 : case JSOP_LAMBDA_FC:
4714 : #if JS_HAS_GENERATOR_EXPRS
4715 495 : sn = js_GetSrcNote(jp->script, pc);
4716 495 : if (sn && SN_TYPE(sn) == SRC_GENEXP) {
4717 : Vector<JSAtom *> *innerLocalNames;
4718 : Vector<JSAtom *> *outerLocalNames;
4719 : JSScript *inner, *outer;
4720 : Vector<DecompiledOpcode> *decompiledOpcodes;
4721 270 : SprintStack ss2(cx);
4722 : JSFunction *outerfun;
4723 :
4724 135 : fun = jp->script->getFunction(GET_UINT32_INDEX(pc));
4725 :
4726 : /*
4727 : * All allocation when decompiling is LIFO, using malloc or,
4728 : * more commonly, arena-allocating from cx->tempLifoAlloc
4729 : * Therefore after InitSprintStack succeeds, we must release
4730 : * to mark before returning.
4731 : */
4732 270 : LifoAllocScope las(&cx->tempLifoAlloc());
4733 135 : if (fun->script()->bindings.hasLocalNames()) {
4734 36 : innerLocalNames = cx->new_<Vector<JSAtom *> >(cx);
4735 72 : if (!innerLocalNames ||
4736 36 : !fun->script()->bindings.getLocalNameArray(cx, innerLocalNames))
4737 : {
4738 0 : return NULL;
4739 : }
4740 : } else {
4741 99 : innerLocalNames = NULL;
4742 : }
4743 135 : inner = fun->script();
4744 135 : if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner)))
4745 0 : return NULL;
4746 135 : ss2.inGenExp = JS_TRUE;
4747 :
4748 : /*
4749 : * Recursively decompile this generator function as an
4750 : * un-parenthesized generator expression. The ss->inGenExp
4751 : * special case of JSOP_YIELD shares array comprehension
4752 : * decompilation code that leaves the result as the single
4753 : * string pushed on ss2.
4754 : */
4755 135 : outer = jp->script;
4756 135 : outerfun = jp->fun;
4757 135 : outerLocalNames = jp->localNames;
4758 135 : decompiledOpcodes = jp->decompiledOpcodes;
4759 135 : LOCAL_ASSERT(UnsignedPtrDiff(pc, outer->code) <= outer->length);
4760 135 : jp->script = inner;
4761 135 : jp->fun = fun;
4762 135 : jp->localNames = innerLocalNames;
4763 135 : jp->decompiledOpcodes = NULL;
4764 :
4765 : /*
4766 : * Decompile only the main bytecode, to avoid tripping over
4767 : * new prolog ops that have stack effects.
4768 : */
4769 135 : ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset)
4770 135 : != NULL;
4771 135 : jp->script = outer;
4772 135 : jp->fun = outerfun;
4773 135 : jp->localNames = outerLocalNames;
4774 135 : jp->decompiledOpcodes = decompiledOpcodes;
4775 135 : if (!ok)
4776 0 : return NULL;
4777 :
4778 : /*
4779 : * Advance over this op and its global |this| push, and
4780 : * arrange to advance over the call to this lambda.
4781 : */
4782 135 : pc += len;
4783 135 : LOCAL_ASSERT(*pc == JSOP_UNDEFINED);
4784 135 : pc += JSOP_UNDEFINED_LENGTH;
4785 135 : LOCAL_ASSERT(*pc == JSOP_CALL);
4786 135 : LOCAL_ASSERT(GET_ARGC(pc) == 0);
4787 135 : len = JSOP_CALL_LENGTH;
4788 :
4789 : /*
4790 : * Arrange to parenthesize this genexp unless:
4791 : *
4792 : * 1. It is the complete expression consumed by a control
4793 : * flow bytecode such as JSOP_TABLESWITCH whose syntax
4794 : * always parenthesizes the controlling expression.
4795 : * 2. It is the sole argument to a function call.
4796 : *
4797 : * But if this genexp runs up against endpc, parenthesize
4798 : * regardless. (This can happen if we are called from
4799 : * DecompileExpression or recursively from case
4800 : * JSOP_{NOP,AND,OR}.)
4801 : *
4802 : * There's no special case for |if (genexp)| because the
4803 : * compiler optimizes that to |if (true)|.
4804 : */
4805 135 : pc2 = pc + len;
4806 135 : op = JSOp(*pc2);
4807 135 : if (op == JSOP_LOOPHEAD || op == JSOP_NOP)
4808 0 : pc2 += JSOP_NOP_LENGTH;
4809 135 : LOCAL_ASSERT(pc2 < endpc ||
4810 : endpc < outer->code + outer->length);
4811 135 : LOCAL_ASSERT(ss2.top == 1);
4812 135 : ss2.opcodes[0] = JSOP_POP;
4813 135 : if (pc2 == endpc) {
4814 0 : op = JSOP_SETNAME;
4815 : } else {
4816 135 : op = (JSOp) *pc2;
4817 : op = ((js_CodeSpec[op].format & JOF_PARENHEAD) ||
4818 0 : ((js_CodeSpec[op].format & JOF_INVOKE) && GET_ARGC(pc2) == 1))
4819 : ? JSOP_POP
4820 135 : : JSOP_SETNAME;
4821 :
4822 : /*
4823 : * Stack this result as if it's a name and not an
4824 : * anonymous function, so it doesn't get decompiled as
4825 : * a generator function in a getter or setter context.
4826 : * The precedence level is the same for JSOP_NAME and
4827 : * JSOP_LAMBDA.
4828 : */
4829 135 : LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
4830 : js_CodeSpec[saveop].prec);
4831 135 : saveop = JSOP_NAME;
4832 : }
4833 :
4834 : /*
4835 : * Alas, we have to malloc a copy of the result left on the
4836 : * top of ss2 because both ss and ss2 arena-allocate from
4837 : * cx's tempLifoAlloc
4838 : */
4839 135 : rval = JS_strdup(cx, PopStr(&ss2, op));
4840 135 : las.releaseEarly();
4841 135 : if (!rval)
4842 0 : return NULL;
4843 135 : todo = ss->sprinter.put(rval);
4844 135 : cx->free_((void *)rval);
4845 135 : break;
4846 : }
4847 : #endif /* JS_HAS_GENERATOR_EXPRS */
4848 : /* FALL THROUGH */
4849 :
4850 360 : fun = jp->script->getFunction(GET_UINT32_INDEX(pc));
4851 : {
4852 : /*
4853 : * Always parenthesize expression closures. We can't force
4854 : * saveop to a low-precedence op to arrange for auto-magic
4855 : * parenthesization without confusing getter/setter code
4856 : * that checks for JSOP_LAMBDA.
4857 : */
4858 360 : bool grouped = !(fun->flags & JSFUN_EXPR_CLOSURE);
4859 360 : bool strict = jp->script->strictModeCode;
4860 : str = js_DecompileToString(cx, "lambda", fun, 0,
4861 : false, grouped, strict,
4862 360 : js_DecompileFunction);
4863 360 : if (!str)
4864 0 : return NULL;
4865 : }
4866 : sprint_string:
4867 378 : todo = ss->sprinter.putString(str);
4868 378 : break;
4869 :
4870 : case JSOP_CALLEE:
4871 54 : JS_ASSERT(jp->fun && jp->fun->atom);
4872 54 : todo = ss->sprinter.putString(jp->fun->atom);
4873 54 : break;
4874 :
4875 : case JSOP_OBJECT:
4876 18 : obj = jp->script->getObject(GET_UINT32_INDEX(pc));
4877 18 : str = js_ValueToSource(cx, ObjectValue(*obj));
4878 18 : if (!str)
4879 0 : return NULL;
4880 18 : goto sprint_string;
4881 :
4882 : case JSOP_REGEXP:
4883 0 : obj = jp->script->getRegExp(GET_UINT32_INDEX(pc));
4884 0 : str = obj->asRegExp().toString(cx);
4885 0 : if (!str)
4886 0 : return NULL;
4887 0 : goto sprint_string;
4888 :
4889 : case JSOP_TABLESWITCH:
4890 : {
4891 : ptrdiff_t off, off2;
4892 : int32_t j, n, low, high;
4893 : TableEntry *table, *tmp;
4894 :
4895 108 : sn = js_GetSrcNote(jp->script, pc);
4896 108 : LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4897 108 : len = js_GetSrcNoteOffset(sn, 0);
4898 108 : off = GET_JUMP_OFFSET(pc);
4899 108 : pc2 = pc + JUMP_OFFSET_LEN;
4900 108 : low = GET_JUMP_OFFSET(pc2);
4901 108 : pc2 += JUMP_OFFSET_LEN;
4902 108 : high = GET_JUMP_OFFSET(pc2);
4903 108 : pc2 += JUMP_OFFSET_LEN;
4904 :
4905 108 : n = high - low + 1;
4906 108 : if (n == 0) {
4907 0 : table = NULL;
4908 0 : j = 0;
4909 0 : ok = true;
4910 : } else {
4911 : table = (TableEntry *)
4912 108 : cx->malloc_((size_t)n * sizeof *table);
4913 108 : if (!table)
4914 0 : return NULL;
4915 243 : for (i = j = 0; i < n; i++) {
4916 135 : table[j].label = NULL;
4917 135 : off2 = GET_JUMP_OFFSET(pc2);
4918 135 : if (off2) {
4919 135 : sn = js_GetSrcNote(jp->script, pc2);
4920 135 : if (sn) {
4921 0 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4922 0 : GET_SOURCE_NOTE_ATOM(sn, table[j].label);
4923 : }
4924 135 : table[j].key = INT_TO_JSVAL(low + i);
4925 135 : table[j].offset = off2;
4926 135 : table[j].order = j;
4927 135 : j++;
4928 : }
4929 135 : pc2 += JUMP_OFFSET_LEN;
4930 : }
4931 : tmp = (TableEntry *)
4932 108 : cx->malloc_((size_t)j * sizeof *table);
4933 108 : if (tmp) {
4934 108 : MergeSort(table, size_t(j), tmp, CompareTableEntries);
4935 108 : Foreground::free_(tmp);
4936 108 : ok = true;
4937 : } else {
4938 0 : ok = false;
4939 : }
4940 : }
4941 :
4942 108 : if (ok)
4943 108 : ok = DecompileSwitch(ss, table, (unsigned)j, pc, len, off, false);
4944 108 : cx->free_(table);
4945 108 : if (!ok)
4946 0 : return NULL;
4947 108 : todo = -2;
4948 108 : break;
4949 : }
4950 :
4951 : case JSOP_LOOKUPSWITCH:
4952 : {
4953 : ptrdiff_t off, off2;
4954 : jsatomid npairs, k;
4955 : TableEntry *table;
4956 :
4957 9 : sn = js_GetSrcNote(jp->script, pc);
4958 9 : LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4959 9 : len = js_GetSrcNoteOffset(sn, 0);
4960 9 : off = GET_JUMP_OFFSET(pc);
4961 9 : pc2 = pc + JUMP_OFFSET_LEN;
4962 9 : npairs = GET_UINT16(pc2);
4963 9 : pc2 += UINT16_LEN;
4964 :
4965 : table = (TableEntry *)
4966 9 : cx->malloc_((size_t)npairs * sizeof *table);
4967 9 : if (!table)
4968 0 : return NULL;
4969 27 : for (k = 0; k < npairs; k++) {
4970 18 : sn = js_GetSrcNote(jp->script, pc2);
4971 18 : if (sn) {
4972 0 : LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4973 0 : GET_SOURCE_NOTE_ATOM(sn, table[k].label);
4974 : } else {
4975 18 : table[k].label = NULL;
4976 : }
4977 18 : uint32_t constIndex = GET_UINT32_INDEX(pc2);
4978 18 : pc2 += UINT32_INDEX_LEN;
4979 18 : off2 = GET_JUMP_OFFSET(pc2);
4980 18 : pc2 += JUMP_OFFSET_LEN;
4981 18 : table[k].key = jp->script->getConst(constIndex);
4982 18 : table[k].offset = off2;
4983 : }
4984 :
4985 : ok = DecompileSwitch(ss, table, (unsigned)npairs, pc, len, off,
4986 9 : JS_FALSE);
4987 9 : cx->free_(table);
4988 9 : if (!ok)
4989 0 : return NULL;
4990 9 : todo = -2;
4991 9 : break;
4992 : }
4993 :
4994 : case JSOP_CONDSWITCH:
4995 : {
4996 : ptrdiff_t off, off2, caseOff;
4997 : int ncases;
4998 : TableEntry *table;
4999 :
5000 0 : sn = js_GetSrcNote(jp->script, pc);
5001 0 : LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
5002 0 : len = js_GetSrcNoteOffset(sn, 0);
5003 0 : off = js_GetSrcNoteOffset(sn, 1);
5004 :
5005 : /*
5006 : * Count the cases using offsets from switch to first case,
5007 : * and case to case, stored in srcnote immediates.
5008 : */
5009 0 : pc2 = pc;
5010 0 : off2 = off;
5011 0 : for (ncases = 0; off2 != 0; ncases++) {
5012 0 : pc2 += off2;
5013 0 : LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);
5014 0 : if (*pc2 == JSOP_DEFAULT) {
5015 : /* End of cases, but count default as a case. */
5016 0 : off2 = 0;
5017 : } else {
5018 0 : sn = js_GetSrcNote(jp->script, pc2);
5019 0 : LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
5020 0 : off2 = js_GetSrcNoteOffset(sn, 0);
5021 : }
5022 : }
5023 :
5024 : /*
5025 : * Allocate table and rescan the cases using their srcnotes,
5026 : * stashing each case's delta from switch top in table[i].key,
5027 : * and the distance to its statements in table[i].offset.
5028 : */
5029 : table = (TableEntry *)
5030 0 : cx->malloc_((size_t)ncases * sizeof *table);
5031 0 : if (!table)
5032 0 : return NULL;
5033 0 : pc2 = pc;
5034 0 : off2 = off;
5035 0 : for (i = 0; i < ncases; i++) {
5036 0 : pc2 += off2;
5037 0 : LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);
5038 0 : caseOff = pc2 - pc;
5039 0 : table[i].key = INT_TO_JSVAL((int32_t) caseOff);
5040 0 : table[i].offset = caseOff + GET_JUMP_OFFSET(pc2);
5041 0 : if (*pc2 == JSOP_CASE) {
5042 0 : sn = js_GetSrcNote(jp->script, pc2);
5043 0 : LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
5044 0 : off2 = js_GetSrcNoteOffset(sn, 0);
5045 : }
5046 : }
5047 :
5048 : /*
5049 : * Find offset of default code by fetching the default offset
5050 : * from the end of table. JSOP_CONDSWITCH always has a default
5051 : * case at the end.
5052 : */
5053 0 : off = JSVAL_TO_INT(table[ncases-1].key);
5054 0 : pc2 = pc + off;
5055 0 : off += GET_JUMP_OFFSET(pc2);
5056 :
5057 : ok = DecompileSwitch(ss, table, (unsigned)ncases, pc, len, off,
5058 0 : JS_TRUE);
5059 0 : cx->free_(table);
5060 0 : if (!ok)
5061 0 : return NULL;
5062 0 : todo = -2;
5063 0 : break;
5064 : }
5065 :
5066 : case JSOP_CASE:
5067 : {
5068 0 : lval = PopStr(ss, op, &lvalpc);
5069 0 : if (!lval)
5070 0 : return NULL;
5071 0 : js_printf(jp, "\tcase ");
5072 0 : SprintOpcodePermanent(jp, lval, lvalpc);
5073 0 : js_printf(jp, ":\n");
5074 0 : todo = -2;
5075 0 : break;
5076 : }
5077 :
5078 : case JSOP_DEFFUN:
5079 0 : fun = jp->script->getFunction(GET_UINT32_INDEX(pc));
5080 0 : todo = -2;
5081 0 : goto do_function;
5082 :
5083 : case JSOP_HOLE:
5084 0 : todo = ss->sprinter.put("", 0);
5085 0 : break;
5086 :
5087 : case JSOP_NEWINIT:
5088 : {
5089 679 : i = GET_UINT8(pc);
5090 679 : LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
5091 :
5092 679 : todo = ss->sprinter.getOffset();
5093 679 : if (i == JSProto_Array) {
5094 126 : ++ss->inArrayInit;
5095 126 : if (ss->sprinter.put("[") < 0)
5096 0 : return NULL;
5097 : } else {
5098 553 : if (ss->sprinter.put("{") < 0)
5099 0 : return NULL;
5100 : }
5101 679 : break;
5102 : }
5103 :
5104 : case JSOP_NEWARRAY:
5105 : {
5106 1309 : todo = ss->sprinter.getOffset();
5107 1309 : ++ss->inArrayInit;
5108 1309 : if (ss->sprinter.put("[") < 0)
5109 0 : return NULL;
5110 1309 : break;
5111 : }
5112 :
5113 : case JSOP_NEWOBJECT:
5114 : {
5115 72 : todo = ss->sprinter.getOffset();
5116 72 : if (ss->sprinter.put("{") < 0)
5117 0 : return NULL;
5118 72 : break;
5119 : }
5120 :
5121 : case JSOP_ENDINIT:
5122 : {
5123 : JSBool inArray;
5124 :
5125 2060 : op = JSOP_NOP; /* turn off parens */
5126 2060 : rval = PopStr(ss, op, &rvalpc);
5127 2060 : sn = js_GetSrcNote(jp->script, pc);
5128 :
5129 : /* Skip any #n= prefix to find the opening bracket. */
5130 2060 : for (xval = rval; *xval != '[' && *xval != '{'; xval++)
5131 0 : continue;
5132 2060 : inArray = (*xval == '[');
5133 2060 : if (inArray)
5134 1435 : --ss->inArrayInit;
5135 2060 : todo = ss->sprinter.getOffset();
5136 2060 : SprintOpcode(ss, rval, rvalpc, pc, todo);
5137 : Sprint(&ss->sprinter, "%s%c",
5138 : (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
5139 2060 : inArray ? ']' : '}');
5140 2060 : break;
5141 : }
5142 :
5143 : {
5144 : JSBool isFirst;
5145 : const char *maybeComma;
5146 :
5147 : case JSOP_INITELEM:
5148 1677 : isFirst = IsInitializerOp(ss->opcodes[ss->top - 3]);
5149 :
5150 : /* Turn off most parens. */
5151 1677 : rval = PopStr(ss, JSOP_SETNAME, &rvalpc);
5152 :
5153 : /* Turn off all parens for xval and lval, which we control. */
5154 1677 : xval = PopStr(ss, JSOP_NOP);
5155 1677 : lval = PopStr(ss, JSOP_NOP, &lvalpc);
5156 1677 : sn = js_GetSrcNote(jp->script, pc);
5157 :
5158 1677 : if (sn && SN_TYPE(sn) == SRC_INITPROP) {
5159 54 : atom = NULL;
5160 54 : goto do_initprop;
5161 : }
5162 1623 : maybeComma = isFirst ? "" : ", ";
5163 1623 : todo = Sprint(&ss->sprinter, "%s%s", lval, maybeComma);
5164 1623 : SprintOpcode(ss, rval, rvalpc, pc, todo);
5165 1623 : break;
5166 :
5167 : case JSOP_INITPROP:
5168 : case JSOP_INITMETHOD:
5169 677 : LOAD_ATOM(0);
5170 677 : xval = QuoteString(&ss->sprinter, atom, jschar(IsIdentifier(atom) ? 0 : '\''));
5171 677 : if (!xval)
5172 0 : return NULL;
5173 677 : isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]);
5174 677 : rval = PopStrDupe(ss, op, &rvalpc);
5175 677 : lval = PopStr(ss, op, &lvalpc);
5176 : /* fall through */
5177 :
5178 : do_initprop:
5179 731 : todo = ss->sprinter.getOffset();
5180 731 : SprintOpcode(ss, lval, lvalpc, pc, todo);
5181 731 : maybeComma = isFirst ? "" : ", ";
5182 731 : if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
5183 0 : const char *end = rval + strlen(rval);
5184 :
5185 0 : if (*rval == '(')
5186 0 : ++rval, --end;
5187 0 : LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0);
5188 0 : LOCAL_ASSERT(rval[8] == ' ');
5189 0 : rval += 8 + 1;
5190 0 : LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}');
5191 : Sprint(&ss->sprinter, "%s%s %s%s%.*s",
5192 : maybeComma,
5193 : (lastop == JSOP_GETTER)
5194 : ? js_get_str : js_set_str,
5195 : xval,
5196 0 : (rval[0] != '(') ? " " : "",
5197 0 : end - rval, rval);
5198 : } else {
5199 731 : Sprint(&ss->sprinter, "%s%s: ", maybeComma, xval);
5200 731 : SprintOpcode(ss, rval, rvalpc, pc, todo);
5201 : }
5202 731 : break;
5203 : }
5204 :
5205 : case JSOP_DEBUGGER:
5206 157 : js_printf(jp, "\tdebugger;\n");
5207 157 : todo = -2;
5208 157 : break;
5209 :
5210 : #if JS_HAS_XML_SUPPORT
5211 : case JSOP_STARTXML:
5212 : case JSOP_STARTXMLEXPR:
5213 0 : inXML = op == JSOP_STARTXML;
5214 0 : todo = -2;
5215 0 : break;
5216 :
5217 : case JSOP_DEFXMLNS:
5218 0 : rval = POP_STR();
5219 : js_printf(jp, "\t%s %s %s = %s;\n",
5220 0 : js_default_str, js_xml_str, js_namespace_str, rval);
5221 0 : todo = -2;
5222 0 : break;
5223 :
5224 : case JSOP_ANYNAME:
5225 9 : if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
5226 0 : len += JSOP_TOATTRNAME_LENGTH;
5227 0 : todo = ss->sprinter.put("@*", 2);
5228 : } else {
5229 9 : todo = ss->sprinter.put("*", 1);
5230 : }
5231 9 : break;
5232 :
5233 : case JSOP_QNAMEPART:
5234 36 : LOAD_ATOM(0);
5235 36 : if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
5236 9 : saveop = JSOP_TOATTRNAME;
5237 9 : len += JSOP_TOATTRNAME_LENGTH;
5238 9 : lval = "@";
5239 9 : goto do_qname;
5240 : }
5241 27 : goto do_name;
5242 :
5243 : case JSOP_QNAMECONST:
5244 9 : LOAD_ATOM(0);
5245 9 : rval = QuoteString(&ss->sprinter, atom, 0);
5246 9 : if (!rval)
5247 0 : return NULL;
5248 9 : ss->sprinter.setOffset(rval);
5249 9 : lval = POP_STR();
5250 9 : todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
5251 9 : break;
5252 :
5253 : case JSOP_QNAME:
5254 0 : rval = POP_STR();
5255 0 : lval = POP_STR();
5256 0 : todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
5257 0 : break;
5258 :
5259 : case JSOP_TOATTRNAME:
5260 0 : op = JSOP_NOP; /* turn off parens */
5261 0 : rval = POP_STR();
5262 0 : todo = Sprint(&ss->sprinter, "@[%s]", rval);
5263 0 : break;
5264 :
5265 : case JSOP_TOATTRVAL:
5266 0 : todo = -2;
5267 0 : break;
5268 :
5269 : case JSOP_ADDATTRNAME:
5270 0 : rval = POP_STR();
5271 0 : lval = POP_STR();
5272 0 : todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
5273 : /* This gets reset by all XML tag expressions. */
5274 0 : quoteAttr = JS_TRUE;
5275 0 : break;
5276 :
5277 : case JSOP_ADDATTRVAL:
5278 0 : rval = POP_STR();
5279 0 : lval = POP_STR();
5280 0 : if (quoteAttr)
5281 0 : todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
5282 : else
5283 0 : todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
5284 0 : break;
5285 :
5286 : case JSOP_BINDXMLNAME:
5287 : /* Leave the name stacked and push a dummy string. */
5288 9 : todo = Sprint(&ss->sprinter, "");
5289 9 : break;
5290 :
5291 : case JSOP_SETXMLNAME:
5292 : /* Pop the r.h.s., the dummy string, and the name. */
5293 0 : rval = PopStrDupe(ss, op, &rvalpc);
5294 0 : (void) PopOff(ss, op);
5295 0 : lval = POP_STR();
5296 0 : goto do_setlval;
5297 :
5298 : case JSOP_XMLELTEXPR:
5299 : case JSOP_XMLTAGEXPR:
5300 0 : todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
5301 0 : inXML = JS_TRUE;
5302 : /* If we're an attribute value, we shouldn't quote this. */
5303 0 : quoteAttr = JS_FALSE;
5304 0 : break;
5305 :
5306 : case JSOP_TOXMLLIST:
5307 0 : op = JSOP_NOP; /* turn off parens */
5308 0 : todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
5309 0 : inXML = JS_FALSE;
5310 0 : break;
5311 :
5312 : case JSOP_TOXML:
5313 : case JSOP_CALLXMLNAME:
5314 : case JSOP_XMLNAME:
5315 : case JSOP_FILTER:
5316 : /* These ops indicate the end of XML expressions. */
5317 9 : inXML = JS_FALSE;
5318 9 : todo = -2;
5319 9 : break;
5320 :
5321 : case JSOP_ENDFILTER:
5322 0 : rval = POP_STR();
5323 0 : PROPAGATE_CALLNESS();
5324 0 : lval = POP_STR();
5325 0 : todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
5326 0 : break;
5327 :
5328 : case JSOP_DESCENDANTS:
5329 0 : rval = POP_STR();
5330 0 : PROPAGATE_CALLNESS();
5331 0 : lval = POP_STR();
5332 0 : todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
5333 0 : break;
5334 :
5335 : case JSOP_XMLCDATA:
5336 0 : LOAD_ATOM(0);
5337 0 : todo = ss->sprinter.put("<![CDATA[", 9);
5338 0 : if (!QuoteString(&ss->sprinter, atom, DONT_ESCAPE))
5339 0 : return NULL;
5340 0 : ss->sprinter.put("]]>", 3);
5341 0 : break;
5342 :
5343 : case JSOP_XMLCOMMENT:
5344 0 : LOAD_ATOM(0);
5345 0 : todo = ss->sprinter.put("<!--", 4);
5346 0 : if (!QuoteString(&ss->sprinter, atom, DONT_ESCAPE))
5347 0 : return NULL;
5348 0 : ss->sprinter.put("-->", 3);
5349 0 : break;
5350 :
5351 : case JSOP_XMLPI:
5352 0 : LOAD_ATOM(0);
5353 0 : rval = JS_strdup(cx, POP_STR());
5354 0 : if (!rval)
5355 0 : return NULL;
5356 0 : todo = ss->sprinter.put("<?", 2);
5357 0 : ok = QuoteString(&ss->sprinter, atom, 0) &&
5358 : (*rval == '\0' ||
5359 0 : (ss->sprinter.put(" ", 1) >= 0 &&
5360 0 : ss->sprinter.put(rval)));
5361 0 : cx->free_((char *)rval);
5362 0 : if (!ok)
5363 0 : return NULL;
5364 0 : ss->sprinter.put("?>", 2);
5365 0 : break;
5366 :
5367 : case JSOP_GETFUNNS:
5368 0 : todo = ss->sprinter.put(js_function_str, 8);
5369 0 : break;
5370 : #endif /* JS_HAS_XML_SUPPORT */
5371 :
5372 : default:
5373 15749 : todo = -2;
5374 15749 : break;
5375 : }
5376 : }
5377 :
5378 130892 : if (cx->isExceptionPending()) {
5379 : /* OOMs while printing to a string do not immediately return. */
5380 0 : return NULL;
5381 : }
5382 :
5383 130892 : if (todo < 0) {
5384 : /* -2 means "don't push", -1 means reported error. */
5385 58133 : JS_ASSERT(todo == -2);
5386 58133 : if (todo == -1)
5387 0 : return NULL;
5388 : } else {
5389 72759 : if (!UpdateDecompiledText(ss, pushpc, todo))
5390 0 : return NULL;
5391 72759 : if (!PushOff(ss, todo, saveop, pushpc))
5392 0 : return NULL;
5393 72759 : if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
5394 77 : CopyDecompiledTextForDecomposedOp(jp, pc);
5395 : }
5396 :
5397 130892 : if (op == JSOP_CALLXMLNAME) {
5398 9 : todo = Sprint(&ss->sprinter, "");
5399 9 : if (todo < 0 || !PushOff(ss, todo, saveop))
5400 0 : return NULL;
5401 : }
5402 :
5403 130892 : pc += len;
5404 : }
5405 :
5406 : /*
5407 : * Undefine local macros.
5408 : */
5409 : #undef inXML
5410 : #undef DECOMPILE_CODE
5411 : #undef TOP_STR
5412 : #undef POP_STR
5413 : #undef POP_STR_PREC
5414 : #undef LOCAL_ASSERT
5415 : #undef GET_QUOTE_AND_FMT
5416 : #undef GET_ATOM_QUOTE_AND_FMT
5417 :
5418 21169 : return pc;
5419 : }
5420 :
5421 : static JSBool
5422 10943 : DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, unsigned len,
5423 : unsigned pcdepth)
5424 : {
5425 10943 : JSContext *cx = jp->sprinter.context;
5426 :
5427 10943 : unsigned depth = StackDepth(script);
5428 10943 : JS_ASSERT(pcdepth <= depth);
5429 :
5430 : /* Initialize a sprinter for use with the offset stack. */
5431 21886 : LifoAllocScope las(&cx->tempLifoAlloc());
5432 21886 : SprintStack ss(cx);
5433 10943 : if (!InitSprintStack(cx, &ss, jp, depth))
5434 0 : return false;
5435 :
5436 : /*
5437 : * If we are called from js_DecompileValueGenerator with a portion of
5438 : * script's bytecode that starts with a non-zero model stack depth given
5439 : * by pcdepth, attempt to initialize the missing string offsets in ss to
5440 : * |spindex| negative indexes from fp->sp for the activation fp in which
5441 : * the error arose.
5442 : *
5443 : * See js_DecompileValueGenerator for how its |spindex| parameter is used,
5444 : * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
5445 : * potentially stored below.
5446 : */
5447 10943 : ss.top = pcdepth;
5448 10943 : if (pcdepth != 0) {
5449 3061 : for (unsigned i = 0; i < pcdepth; i++) {
5450 2255 : ss.offsets[i] = -2 - (ptrdiff_t)i;
5451 2255 : ss.opcodes[i] = *jp->pcstack[i];
5452 : }
5453 : }
5454 :
5455 : /* Call recursive subroutine to do the hard work. */
5456 10943 : JSScript *oldscript = jp->script;
5457 10943 : jp->script = script;
5458 10943 : bool ok = Decompile(&ss, pc, len) != NULL;
5459 10943 : jp->script = oldscript;
5460 :
5461 : /* If the given code didn't empty the stack, do it now. */
5462 10943 : if (ok && ss.top) {
5463 : const char *last;
5464 3091 : do {
5465 3091 : last = ss.sprinter.stringAt(PopOff(&ss, JSOP_POP));
5466 : } while (ss.top > pcdepth);
5467 2448 : js_printf(jp, "%s", last);
5468 : }
5469 :
5470 10943 : return ok;
5471 : }
5472 :
5473 : /*
5474 : * Decompile a function body, expression closure expression, or complete
5475 : * script. Start at |pc|; go to the end of |script|. Include a directive
5476 : * prologue, if appropriate.
5477 : */
5478 : static JSBool
5479 8495 : DecompileBody(JSPrinter *jp, JSScript *script, jsbytecode *pc)
5480 : {
5481 : /* Print a strict mode code directive, if needed. */
5482 8495 : if (script->strictModeCode && !jp->strict) {
5483 136 : if (jp->fun && (jp->fun->flags & JSFUN_EXPR_CLOSURE)) {
5484 : /*
5485 : * We have no syntax for strict function expressions;
5486 : * at least give a hint.
5487 : */
5488 0 : js_printf(jp, "\t/* use strict */ \n");
5489 : } else {
5490 136 : js_printf(jp, "\t\"use strict\";\n");
5491 : }
5492 136 : jp->strict = true;
5493 : }
5494 :
5495 8495 : jsbytecode *end = script->code + script->length;
5496 8495 : return DecompileCode(jp, script, pc, end - pc, 0);
5497 : }
5498 :
5499 : JSBool
5500 0 : js_DecompileScript(JSPrinter *jp, JSScript *script)
5501 : {
5502 0 : return DecompileBody(jp, script, script->code);
5503 : }
5504 :
5505 : JSString *
5506 12884 : js_DecompileToString(JSContext *cx, const char *name, JSFunction *fun,
5507 : unsigned indent, JSBool pretty, JSBool grouped, JSBool strict,
5508 : JSDecompilerPtr decompiler)
5509 : {
5510 : JSPrinter *jp;
5511 : JSString *str;
5512 :
5513 12884 : jp = js_NewPrinter(cx, name, fun, indent, pretty, grouped, strict);
5514 12884 : if (!jp)
5515 0 : return NULL;
5516 12884 : if (decompiler(jp))
5517 12884 : str = js_GetPrinterOutput(jp);
5518 : else
5519 0 : str = NULL;
5520 12884 : js_DestroyPrinter(jp);
5521 12884 : return str;
5522 : }
5523 :
5524 : static const char native_code_str[] = "\t[native code]\n";
5525 :
5526 : JSBool
5527 0 : js_DecompileFunctionBody(JSPrinter *jp)
5528 : {
5529 : JSScript *script;
5530 :
5531 0 : JS_ASSERT(jp->fun);
5532 0 : JS_ASSERT(!jp->script);
5533 0 : if (!jp->fun->isInterpreted()) {
5534 0 : js_printf(jp, native_code_str);
5535 0 : return JS_TRUE;
5536 : }
5537 :
5538 0 : script = jp->fun->script();
5539 0 : return DecompileBody(jp, script, script->code);
5540 : }
5541 :
5542 : JSBool
5543 13010 : js_DecompileFunction(JSPrinter *jp)
5544 : {
5545 13010 : JSFunction *fun = jp->fun;
5546 13010 : JS_ASSERT(fun);
5547 13010 : JS_ASSERT(!jp->script);
5548 :
5549 : /*
5550 : * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
5551 : * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
5552 : * an expression by parenthesizing.
5553 : */
5554 13010 : if (jp->pretty) {
5555 2063 : js_printf(jp, "\t");
5556 : } else {
5557 10947 : if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
5558 7158 : js_puts(jp, "(");
5559 : }
5560 :
5561 13010 : js_printf(jp, "%s ", js_function_str);
5562 13010 : if (fun->atom && !QuoteString(&jp->sprinter, fun->atom, 0))
5563 0 : return JS_FALSE;
5564 13010 : js_puts(jp, "(");
5565 :
5566 13010 : if (!fun->isInterpreted()) {
5567 4515 : js_printf(jp, ") {\n");
5568 4515 : jp->indent += 4;
5569 4515 : js_printf(jp, native_code_str);
5570 4515 : jp->indent -= 4;
5571 4515 : js_printf(jp, "\t}");
5572 : } else {
5573 8495 : JSScript *script = fun->script();
5574 : #if JS_HAS_DESTRUCTURING
5575 16990 : SprintStack ss(jp->sprinter.context);
5576 : #endif
5577 :
5578 : /* Print the parameters. */
5579 8495 : jsbytecode *pc = script->main();
5580 8495 : jsbytecode *endpc = pc + script->length;
5581 8495 : JSBool ok = JS_TRUE;
5582 :
5583 : #if JS_HAS_DESTRUCTURING
5584 8495 : ss.printer = NULL;
5585 8495 : jp->script = script;
5586 : #endif
5587 :
5588 15989 : for (unsigned i = 0; i < fun->nargs; i++) {
5589 7494 : if (i > 0)
5590 308 : js_puts(jp, ", ");
5591 :
5592 7494 : JSAtom *param = GetArgOrVarAtom(jp, i);
5593 :
5594 : #if JS_HAS_DESTRUCTURING
5595 : #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
5596 :
5597 7494 : if (!param) {
5598 : ptrdiff_t todo;
5599 : const char *lval;
5600 :
5601 27 : LOCAL_ASSERT(*pc == JSOP_GETARG);
5602 27 : pc += JSOP_GETARG_LENGTH;
5603 27 : LOCAL_ASSERT(*pc == JSOP_DUP);
5604 27 : if (!ss.printer) {
5605 18 : ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script));
5606 18 : if (!ok)
5607 0 : break;
5608 : }
5609 27 : pc = DecompileDestructuring(&ss, pc, endpc);
5610 27 : if (!pc) {
5611 0 : ok = JS_FALSE;
5612 0 : break;
5613 : }
5614 27 : LOCAL_ASSERT(*pc == JSOP_POP);
5615 27 : pc += JSOP_POP_LENGTH;
5616 27 : lval = PopStr(&ss, JSOP_NOP);
5617 27 : todo = jp->sprinter.put(lval);
5618 27 : if (todo < 0) {
5619 0 : ok = JS_FALSE;
5620 0 : break;
5621 : }
5622 27 : continue;
5623 : }
5624 :
5625 : #undef LOCAL_ASSERT
5626 : #endif
5627 :
5628 7467 : if (!QuoteString(&jp->sprinter, param, 0)) {
5629 0 : ok = JS_FALSE;
5630 0 : break;
5631 : }
5632 : }
5633 :
5634 : #if JS_HAS_DESTRUCTURING
5635 8495 : jp->script = NULL;
5636 : #endif
5637 8495 : if (!ok)
5638 0 : return JS_FALSE;
5639 8495 : js_printf(jp, ") ");
5640 8495 : if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
5641 8468 : js_printf(jp, "{\n");
5642 8468 : jp->indent += 4;
5643 : }
5644 :
5645 8495 : ok = DecompileBody(jp, script, pc);
5646 8495 : if (!ok)
5647 0 : return JS_FALSE;
5648 :
5649 8495 : if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
5650 8468 : jp->indent -= 4;
5651 8468 : js_printf(jp, "\t}");
5652 : }
5653 : }
5654 :
5655 13010 : if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
5656 7158 : js_puts(jp, ")");
5657 :
5658 13010 : return JS_TRUE;
5659 : }
5660 :
5661 : char *
5662 4794 : js_DecompileValueGenerator(JSContext *cx, int spindex, jsval v,
5663 : JSString *fallback)
5664 : {
5665 : StackFrame *fp;
5666 : JSScript *script;
5667 : jsbytecode *pc;
5668 :
5669 0 : JS_ASSERT(spindex < 0 ||
5670 : spindex == JSDVG_IGNORE_STACK ||
5671 4794 : spindex == JSDVG_SEARCH_STACK);
5672 :
5673 4794 : if (!cx->hasfp() || !cx->fp()->isScriptFrame())
5674 1818 : goto do_fallback;
5675 :
5676 2976 : fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
5677 2976 : script = fp->script();
5678 2976 : pc = cx->regs().pc;
5679 2976 : JS_ASSERT(script->code <= pc && pc < script->code + script->length);
5680 :
5681 2976 : if (pc < script->main())
5682 0 : goto do_fallback;
5683 :
5684 2976 : if (spindex != JSDVG_IGNORE_STACK) {
5685 : jsbytecode **pcstack;
5686 :
5687 : /*
5688 : * Prepare computing pcstack containing pointers to opcodes that
5689 : * populated interpreter's stack with its current content.
5690 : */
5691 : pcstack = (jsbytecode **)
5692 2465 : cx->malloc_(StackDepth(script) * sizeof *pcstack);
5693 2465 : if (!pcstack)
5694 0 : return NULL;
5695 2465 : jsbytecode *lastDecomposedPC = NULL;
5696 2465 : int pcdepth = ReconstructPCStack(cx, script, pc, pcstack, &lastDecomposedPC);
5697 2465 : if (pcdepth < 0)
5698 0 : goto release_pcstack;
5699 :
5700 2465 : if (spindex != JSDVG_SEARCH_STACK) {
5701 1192 : JS_ASSERT(spindex < 0);
5702 1192 : pcdepth += spindex;
5703 1192 : if (pcdepth < 0)
5704 0 : goto release_pcstack;
5705 1192 : pc = pcstack[pcdepth];
5706 : } else {
5707 : /*
5708 : * We search from fp->sp to base to find the most recently
5709 : * calculated value matching v under assumption that it is
5710 : * it that caused exception, see bug 328664.
5711 : */
5712 1273 : Value *stackBase = fp->base();
5713 1273 : Value *sp = cx->regs().sp;
5714 1986 : do {
5715 2156 : if (sp == stackBase) {
5716 170 : pcdepth = -1;
5717 170 : goto release_pcstack;
5718 : }
5719 : } while (*--sp != v);
5720 :
5721 : /*
5722 : * The value may have come from beyond stackBase + pcdepth, meaning
5723 : * that it came from a temporary slot pushed by the interpreter or
5724 : * arguments pushed for an Invoke call. Only update pc if beneath
5725 : * stackBase + pcdepth. If above, the value may or may not be
5726 : * produced by the current pc. Since it takes a fairly contrived
5727 : * combination of calls to produce a situation where this is not
5728 : * what we want, we just use the current pc.
5729 : *
5730 : * If we are in the middle of a decomposed opcode, use the outer
5731 : * 'fat' opcode itself. Any source notes for the operation which
5732 : * are needed during decompilation will be associated with the
5733 : * outer opcode.
5734 : */
5735 1103 : if (sp < stackBase + pcdepth) {
5736 1103 : pc = pcstack[sp - stackBase];
5737 1103 : if (lastDecomposedPC) {
5738 : size_t len = GetDecomposeLength(lastDecomposedPC,
5739 73 : js_CodeSpec[*lastDecomposedPC].length);
5740 73 : if (unsigned(pc - lastDecomposedPC) < len)
5741 0 : pc = lastDecomposedPC;
5742 : }
5743 : }
5744 : }
5745 :
5746 : release_pcstack:
5747 2465 : cx->free_(pcstack);
5748 2465 : if (pcdepth < 0)
5749 170 : goto do_fallback;
5750 : }
5751 :
5752 : {
5753 2806 : char *name = DecompileExpression(cx, script, fp->maybeFun(), pc);
5754 2806 : if (name != FAILED_EXPRESSION_DECOMPILER)
5755 2478 : return name;
5756 : }
5757 :
5758 : do_fallback:
5759 2316 : if (!fallback) {
5760 2307 : fallback = js_ValueToSource(cx, v);
5761 2307 : if (!fallback)
5762 0 : return NULL;
5763 : }
5764 2316 : size_t length = fallback->length();
5765 2316 : const jschar *chars = fallback->getChars(cx);
5766 2316 : if (!chars)
5767 0 : return NULL;
5768 2316 : return DeflateString(cx, chars, length);
5769 : }
5770 :
5771 : static char *
5772 2853 : DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
5773 : jsbytecode *pc)
5774 : {
5775 2853 : JS_ASSERT(script->code <= pc && pc < script->code + script->length);
5776 :
5777 2853 : JSOp op = (JSOp) *pc;
5778 :
5779 : /* None of these stack-writing ops generates novel values. */
5780 2853 : JS_ASSERT(op != JSOP_CASE && op != JSOP_DUP && op != JSOP_DUP2);
5781 :
5782 : /*
5783 : * |this| could convert to a very long object initialiser, so cite it by
5784 : * its keyword name instead.
5785 : */
5786 2853 : if (op == JSOP_THIS)
5787 40 : return JS_strdup(cx, js_this_str);
5788 :
5789 : /*
5790 : * JSOP_BINDNAME is special: it generates a value, the base object of a
5791 : * reference. But if it is the generating op for a diagnostic produced by
5792 : * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5793 : * fall back to the base object.
5794 : */
5795 2813 : if (op == JSOP_BINDNAME)
5796 1 : return FAILED_EXPRESSION_DECOMPILER;
5797 :
5798 : /* NAME ops are self-contained, others require left or right context. */
5799 2812 : const JSCodeSpec *cs = &js_CodeSpec[op];
5800 2812 : jsbytecode *begin = pc;
5801 2812 : jsbytecode *end = pc + cs->length;
5802 2812 : switch (JOF_MODE(cs->format)) {
5803 : case JOF_PROP:
5804 : case JOF_ELEM:
5805 : case JOF_XMLNAME:
5806 : case 0: {
5807 1878 : jssrcnote *sn = js_GetSrcNote(script, pc);
5808 1878 : if (!sn)
5809 355 : return FAILED_EXPRESSION_DECOMPILER;
5810 1523 : switch (SN_TYPE(sn)) {
5811 : case SRC_PCBASE:
5812 1514 : begin -= js_GetSrcNoteOffset(sn, 0);
5813 1514 : break;
5814 : case SRC_PCDELTA:
5815 0 : end = begin + js_GetSrcNoteOffset(sn, 0);
5816 0 : begin += cs->length;
5817 0 : break;
5818 : default:
5819 9 : return FAILED_EXPRESSION_DECOMPILER;
5820 : }
5821 1514 : break;
5822 : }
5823 : default:;
5824 : }
5825 :
5826 : /*
5827 : * Include the trailing SWAP when decompiling CALLPROP or CALLELEM ops,
5828 : * so that the result is the entire access rather than the lvalue.
5829 : */
5830 2448 : if (op == JSOP_CALLPROP || op == JSOP_CALLELEM) {
5831 643 : JS_ASSERT(*end == JSOP_SWAP);
5832 643 : end += JSOP_SWAP_LENGTH;
5833 : }
5834 :
5835 2448 : ptrdiff_t len = end - begin;
5836 2448 : if (len <= 0)
5837 0 : return FAILED_EXPRESSION_DECOMPILER;
5838 :
5839 : struct Guard {
5840 : jsbytecode **pcstack;
5841 : JSPrinter *printer;
5842 2448 : Guard() : pcstack(NULL), printer(NULL) {}
5843 2448 : ~Guard() {
5844 2448 : if (printer)
5845 2448 : js_DestroyPrinter(printer);
5846 2448 : Foreground::free_(pcstack);
5847 2448 : }
5848 4896 : } g;
5849 :
5850 2448 : g.pcstack = (jsbytecode **)OffTheBooks::malloc_(StackDepth(script) * sizeof *g.pcstack);
5851 2448 : if (!g.pcstack)
5852 0 : return NULL;
5853 :
5854 2448 : int pcdepth = ReconstructPCStack(cx, script, begin, g.pcstack, NULL);
5855 2448 : if (pcdepth < 0)
5856 0 : return FAILED_EXPRESSION_DECOMPILER;
5857 :
5858 2448 : g.printer = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0, false, false, false);
5859 2448 : if (!g.printer)
5860 0 : return NULL;
5861 :
5862 2448 : g.printer->dvgfence = end;
5863 2448 : g.printer->pcstack = g.pcstack;
5864 2448 : if (!DecompileCode(g.printer, script, begin, (unsigned) len, (unsigned) pcdepth))
5865 0 : return NULL;
5866 :
5867 2448 : return JS_strdup(cx, g.printer->sprinter.string());
5868 : }
5869 :
5870 : unsigned
5871 4105716 : js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
5872 : {
5873 4105716 : return ReconstructPCStack(cx, script, pc, NULL, NULL);
5874 : }
5875 :
5876 : #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5877 :
5878 : static int
5879 35010354 : SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs,
5880 : jsbytecode *pc, jsbytecode **pcstack, unsigned &pcdepth)
5881 : {
5882 35010354 : unsigned nuses = StackUses(script, pc);
5883 35010354 : unsigned ndefs = StackDefs(script, pc);
5884 35010354 : LOCAL_ASSERT(pcdepth >= nuses);
5885 35010354 : pcdepth -= nuses;
5886 35010354 : LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script));
5887 :
5888 : /*
5889 : * Fill the slots that the opcode defines withs its pc unless it just
5890 : * reshuffles the stack. In the latter case we want to preserve the
5891 : * opcode that generated the original value.
5892 : */
5893 35010354 : switch (op) {
5894 : default:
5895 34533975 : if (pcstack) {
5896 450222 : for (unsigned i = 0; i != ndefs; ++i)
5897 197090 : pcstack[pcdepth + i] = pc;
5898 : }
5899 34533975 : break;
5900 :
5901 : case JSOP_CASE:
5902 : /* Keep the switch value. */
5903 1025 : JS_ASSERT(ndefs == 1);
5904 1025 : break;
5905 :
5906 : case JSOP_DUP:
5907 239579 : JS_ASSERT(ndefs == 2);
5908 239579 : if (pcstack)
5909 9751 : pcstack[pcdepth + 1] = pcstack[pcdepth];
5910 239579 : break;
5911 :
5912 : case JSOP_DUP2:
5913 45178 : JS_ASSERT(ndefs == 4);
5914 45178 : if (pcstack) {
5915 4091 : pcstack[pcdepth + 2] = pcstack[pcdepth];
5916 4091 : pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
5917 : }
5918 45178 : break;
5919 :
5920 : case JSOP_SWAP:
5921 190597 : JS_ASSERT(ndefs == 2);
5922 190597 : if (pcstack) {
5923 4819 : jsbytecode *tmp = pcstack[pcdepth + 1];
5924 4819 : pcstack[pcdepth + 1] = pcstack[pcdepth];
5925 4819 : pcstack[pcdepth] = tmp;
5926 : }
5927 190597 : break;
5928 : }
5929 35010354 : pcdepth += ndefs;
5930 35010354 : return pcdepth;
5931 : }
5932 :
5933 : static int
5934 4110629 : ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
5935 : jsbytecode **pcstack, jsbytecode **lastDecomposedPC)
5936 : {
5937 : /*
5938 : * Walk forward from script->main and compute the stack depth and stack of
5939 : * operand-generating opcode PCs in pcstack.
5940 : *
5941 : * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5942 : * FIXME: Optimize to use last empty-stack sequence point.
5943 : */
5944 :
5945 4110629 : LOCAL_ASSERT(script->code <= target && target < script->code + script->length);
5946 4110629 : jsbytecode *pc = script->code;
5947 4110629 : unsigned pcdepth = 0;
5948 : ptrdiff_t oplen;
5949 39190812 : for (; pc < target; pc += oplen) {
5950 35080183 : JSOp op = JSOp(*pc);
5951 35080183 : const JSCodeSpec *cs = &js_CodeSpec[op];
5952 35080183 : oplen = cs->length;
5953 35080183 : if (oplen < 0)
5954 506 : oplen = js_GetVariableBytecodeLength(pc);
5955 :
5956 35080183 : if (cs->format & JOF_DECOMPOSE) {
5957 46564 : if (lastDecomposedPC)
5958 1424 : *lastDecomposedPC = pc;
5959 46564 : continue;
5960 : }
5961 :
5962 : /*
5963 : * A (C ? T : E) expression requires skipping either T (if target is in
5964 : * E) or both T and E (if target is after the whole expression) before
5965 : * adjusting pcdepth based on the JSOP_IFEQ at pc that tests condition
5966 : * C. We know that the stack depth can't change from what it was with
5967 : * C on top of stack.
5968 : */
5969 35033619 : jssrcnote *sn = js_GetSrcNote(script, pc);
5970 35033619 : if (sn && SN_TYPE(sn) == SRC_COND) {
5971 1189 : ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0);
5972 1189 : if (pc + jmpoff < target) {
5973 1189 : pc += jmpoff;
5974 1189 : op = JSOp(*pc);
5975 1189 : JS_ASSERT(op == JSOP_GOTO);
5976 1189 : cs = &js_CodeSpec[op];
5977 1189 : oplen = cs->length;
5978 1189 : JS_ASSERT(oplen > 0);
5979 1189 : ptrdiff_t jmplen = GET_JUMP_OFFSET(pc);
5980 1189 : if (pc + jmplen < target) {
5981 1140 : oplen = (unsigned) jmplen;
5982 1140 : continue;
5983 : }
5984 :
5985 : /*
5986 : * Ok, target lies in E. Manually pop C off the model stack,
5987 : * since we have moved beyond the IFEQ now.
5988 : */
5989 49 : LOCAL_ASSERT(pcdepth != 0);
5990 49 : --pcdepth;
5991 : }
5992 : }
5993 :
5994 : /* Ignore early-exit code, which is annotated SRC_HIDDEN. */
5995 35032479 : if (sn && SN_TYPE(sn) == SRC_HIDDEN)
5996 22125 : continue;
5997 :
5998 35010354 : if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0)
5999 0 : return -1;
6000 :
6001 : }
6002 4110629 : LOCAL_ASSERT(pc == target);
6003 4110629 : return pcdepth;
6004 : }
6005 :
6006 : #undef LOCAL_ASSERT
6007 : #undef LOCAL_ASSERT_RV
6008 :
6009 : namespace js {
6010 :
6011 : bool
6012 4390 : CallResultEscapes(jsbytecode *pc)
6013 : {
6014 : /*
6015 : * If we see any of these sequences, the result is unused:
6016 : * - call / pop
6017 : *
6018 : * If we see any of these sequences, the result is only tested for nullness:
6019 : * - call / ifeq
6020 : * - call / not / ifeq
6021 : */
6022 :
6023 4390 : if (*pc != JSOP_CALL)
6024 1 : return true;
6025 :
6026 4389 : pc += JSOP_CALL_LENGTH;
6027 :
6028 4389 : if (*pc == JSOP_POP)
6029 4366 : return false;
6030 :
6031 23 : if (*pc == JSOP_NOT)
6032 9 : pc += JSOP_NOT_LENGTH;
6033 :
6034 23 : return (*pc != JSOP_IFEQ);
6035 : }
6036 :
6037 : extern bool
6038 2831 : IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset)
6039 : {
6040 : // This could be faster (by following jump instructions if the target is <= offset).
6041 5935728 : for (BytecodeRange r(script); !r.empty(); r.popFront()) {
6042 5935719 : size_t here = r.frontOffset();
6043 5935719 : if (here >= offset)
6044 2822 : return here == offset;
6045 : }
6046 9 : return false;
6047 : }
6048 :
6049 : JS_FRIEND_API(size_t)
6050 0 : GetPCCountScriptCount(JSContext *cx)
6051 : {
6052 0 : JSRuntime *rt = cx->runtime;
6053 :
6054 0 : if (!rt->scriptPCCounters)
6055 0 : return 0;
6056 :
6057 0 : return rt->scriptPCCounters->length();
6058 : }
6059 :
6060 : enum MaybeComma {NO_COMMA, COMMA};
6061 :
6062 : static void
6063 0 : AppendJSONProperty(StringBuffer &buf, const char *name, MaybeComma comma = COMMA)
6064 : {
6065 0 : if (comma)
6066 0 : buf.append(',');
6067 :
6068 0 : buf.append('\"');
6069 0 : buf.appendInflated(name, strlen(name));
6070 0 : buf.appendInflated("\":", 2);
6071 0 : }
6072 :
6073 : static void
6074 0 : AppendArrayJSONProperties(JSContext *cx, StringBuffer &buf,
6075 : double *values, const char **names, unsigned count, MaybeComma &comma)
6076 : {
6077 0 : for (unsigned i = 0; i < count; i++) {
6078 0 : if (values[i]) {
6079 0 : AppendJSONProperty(buf, names[i], comma);
6080 0 : comma = COMMA;
6081 0 : NumberValueToStringBuffer(cx, DoubleValue(values[i]), buf);
6082 : }
6083 : }
6084 0 : }
6085 :
6086 : JS_FRIEND_API(JSString *)
6087 0 : GetPCCountScriptSummary(JSContext *cx, size_t index)
6088 : {
6089 0 : JSRuntime *rt = cx->runtime;
6090 :
6091 0 : if (!rt->scriptPCCounters || index >= rt->scriptPCCounters->length()) {
6092 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL);
6093 0 : return NULL;
6094 : }
6095 :
6096 0 : ScriptOpcodeCountsPair info = (*rt->scriptPCCounters)[index];
6097 0 : JSScript *script = info.script;
6098 :
6099 : /*
6100 : * OOM on buffer appends here will not be caught immediately, but since
6101 : * StringBuffer uses a ContextAllocPolicy will trigger an exception on the
6102 : * context if they occur, which we'll catch before returning.
6103 : */
6104 0 : StringBuffer buf(cx);
6105 :
6106 0 : buf.append('{');
6107 :
6108 0 : AppendJSONProperty(buf, "file", NO_COMMA);
6109 0 : JSString *str = JS_NewStringCopyZ(cx, script->filename);
6110 0 : if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
6111 0 : return NULL;
6112 0 : buf.append(str);
6113 :
6114 0 : AppendJSONProperty(buf, "line");
6115 0 : NumberValueToStringBuffer(cx, Int32Value(script->lineno), buf);
6116 :
6117 0 : if (script->function()) {
6118 0 : JSAtom *atom = script->function()->atom;
6119 0 : if (atom) {
6120 0 : AppendJSONProperty(buf, "name");
6121 0 : if (!(str = JS_ValueToSource(cx, StringValue(atom))))
6122 0 : return NULL;
6123 0 : buf.append(str);
6124 : }
6125 : }
6126 :
6127 0 : double baseTotals[OpcodeCounts::BASE_COUNT] = {0.0};
6128 0 : double accessTotals[OpcodeCounts::ACCESS_COUNT - OpcodeCounts::BASE_COUNT] = {0.0};
6129 0 : double elementTotals[OpcodeCounts::ELEM_COUNT - OpcodeCounts::ACCESS_COUNT] = {0.0};
6130 0 : double propertyTotals[OpcodeCounts::PROP_COUNT - OpcodeCounts::ACCESS_COUNT] = {0.0};
6131 0 : double arithTotals[OpcodeCounts::ARITH_COUNT - OpcodeCounts::BASE_COUNT] = {0.0};
6132 :
6133 0 : for (unsigned i = 0; i < script->length; i++) {
6134 0 : OpcodeCounts &counts = info.getCounts(script->code + i);
6135 0 : if (!counts)
6136 0 : continue;
6137 :
6138 0 : JSOp op = (JSOp)script->code[i];
6139 0 : unsigned numCounts = OpcodeCounts::numCounts(op);
6140 :
6141 0 : for (unsigned j = 0; j < numCounts; j++) {
6142 0 : double value = counts.get(j);
6143 0 : if (j < OpcodeCounts::BASE_COUNT) {
6144 0 : baseTotals[j] += value;
6145 0 : } else if (OpcodeCounts::accessOp(op)) {
6146 0 : if (j < OpcodeCounts::ACCESS_COUNT)
6147 0 : accessTotals[j - OpcodeCounts::BASE_COUNT] += value;
6148 0 : else if (OpcodeCounts::elementOp(op))
6149 0 : elementTotals[j - OpcodeCounts::ACCESS_COUNT] += value;
6150 0 : else if (OpcodeCounts::propertyOp(op))
6151 0 : propertyTotals[j - OpcodeCounts::ACCESS_COUNT] += value;
6152 : else
6153 0 : JS_NOT_REACHED("Bad opcode");
6154 0 : } else if (OpcodeCounts::arithOp(op)) {
6155 0 : arithTotals[j - OpcodeCounts::BASE_COUNT] += value;
6156 : } else {
6157 0 : JS_NOT_REACHED("Bad opcode");
6158 : }
6159 : }
6160 : }
6161 :
6162 0 : AppendJSONProperty(buf, "totals");
6163 0 : buf.append('{');
6164 :
6165 0 : MaybeComma comma = NO_COMMA;
6166 :
6167 : AppendArrayJSONProperties(cx, buf, baseTotals, countBaseNames,
6168 0 : JS_ARRAY_LENGTH(baseTotals), comma);
6169 : AppendArrayJSONProperties(cx, buf, accessTotals, countAccessNames,
6170 0 : JS_ARRAY_LENGTH(accessTotals), comma);
6171 : AppendArrayJSONProperties(cx, buf, elementTotals, countElementNames,
6172 0 : JS_ARRAY_LENGTH(elementTotals), comma);
6173 : AppendArrayJSONProperties(cx, buf, propertyTotals, countPropertyNames,
6174 0 : JS_ARRAY_LENGTH(propertyTotals), comma);
6175 : AppendArrayJSONProperties(cx, buf, arithTotals, countArithNames,
6176 0 : JS_ARRAY_LENGTH(arithTotals), comma);
6177 :
6178 0 : buf.append('}');
6179 0 : buf.append('}');
6180 :
6181 0 : if (cx->isExceptionPending())
6182 0 : return NULL;
6183 :
6184 0 : return buf.finishString();
6185 : }
6186 :
6187 : struct AutoDestroyPrinter
6188 : {
6189 : JSPrinter *jp;
6190 0 : AutoDestroyPrinter(JSPrinter *jp) : jp(jp) {}
6191 0 : ~AutoDestroyPrinter() { js_DestroyPrinter(jp); }
6192 : };
6193 :
6194 : static bool
6195 0 : GetPCCountJSON(JSContext *cx, const ScriptOpcodeCountsPair &info, StringBuffer &buf)
6196 : {
6197 0 : JSScript *script = info.script;
6198 :
6199 0 : buf.append('{');
6200 0 : AppendJSONProperty(buf, "text", NO_COMMA);
6201 :
6202 0 : Vector<DecompiledOpcode> decompiledOpcodes(cx);
6203 0 : if (!decompiledOpcodes.reserve(script->length))
6204 0 : return false;
6205 :
6206 0 : for (unsigned i = 0; i < script->length; i++)
6207 0 : decompiledOpcodes.infallibleAppend(DecompiledOpcode());
6208 :
6209 0 : JSFunction *fun = script->function();
6210 0 : JSPrinter *jp = js_NewPrinter(cx, "", fun, 4, true, false, false);
6211 0 : if (!jp)
6212 0 : return false;
6213 0 : AutoDestroyPrinter destroy(jp);
6214 :
6215 0 : jp->decompiledOpcodes = &decompiledOpcodes;
6216 :
6217 0 : if (fun) {
6218 0 : if (!js_DecompileFunction(jp))
6219 0 : return false;
6220 : } else {
6221 0 : if (!js_DecompileScript(jp, script))
6222 0 : return false;
6223 : }
6224 0 : JSString *str = js_GetPrinterOutput(jp);
6225 0 : if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
6226 0 : return false;
6227 :
6228 0 : buf.append(str);
6229 :
6230 0 : AppendJSONProperty(buf, "opcodes");
6231 0 : buf.append('[');
6232 0 : bool comma = false;
6233 :
6234 0 : SrcNoteLineScanner scanner(script->notes(), script->lineno);
6235 :
6236 0 : for (jsbytecode *pc = script->code;
6237 0 : pc < script->code + script->length;
6238 0 : pc += GetBytecodeLength(pc))
6239 : {
6240 0 : size_t offset = pc - script->code;
6241 :
6242 0 : JSOp op = (JSOp) *pc;
6243 :
6244 0 : if (comma)
6245 0 : buf.append(',');
6246 0 : comma = true;
6247 :
6248 0 : buf.append('{');
6249 :
6250 0 : AppendJSONProperty(buf, "id", NO_COMMA);
6251 0 : NumberValueToStringBuffer(cx, Int32Value(pc - script->code), buf);
6252 :
6253 0 : scanner.advanceTo(offset);
6254 :
6255 0 : AppendJSONProperty(buf, "line");
6256 0 : NumberValueToStringBuffer(cx, Int32Value(scanner.getLine()), buf);
6257 :
6258 : {
6259 0 : const char *name = js_CodeName[op];
6260 0 : AppendJSONProperty(buf, "name");
6261 0 : buf.append('\"');
6262 0 : buf.appendInflated(name, strlen(name));
6263 0 : buf.append('\"');
6264 : }
6265 :
6266 0 : DecompiledOpcode *search = &decompiledOpcodes[offset];
6267 0 : size_t textBias = 0;
6268 0 : while (search->parent) {
6269 0 : textBias += search->parentOffset;
6270 0 : if (search->parenthesized)
6271 0 : textBias++;
6272 0 : search = &decompiledOpcodes[search->parent - script->code];
6273 : }
6274 :
6275 0 : int32_t printedOffset = search->parentOffset;
6276 0 : if (printedOffset != -1) {
6277 0 : printedOffset += textBias;
6278 0 : if (search->parenthesized)
6279 0 : printedOffset++;
6280 0 : AppendJSONProperty(buf, "textOffset");
6281 0 : NumberValueToStringBuffer(cx, Int32Value(printedOffset), buf);
6282 : }
6283 :
6284 0 : const char *text = decompiledOpcodes[offset].text;
6285 0 : if (text && *text != 0) {
6286 0 : AppendJSONProperty(buf, "text");
6287 0 : JSString *str = JS_NewStringCopyZ(cx, text);
6288 0 : if (!str || !(str = JS_ValueToSource(cx, StringValue(str))))
6289 0 : return false;
6290 0 : buf.append(str);
6291 : }
6292 :
6293 0 : OpcodeCounts &counts = info.getCounts(pc);
6294 0 : unsigned numCounts = OpcodeCounts::numCounts(op);
6295 :
6296 0 : AppendJSONProperty(buf, "counts");
6297 0 : buf.append('{');
6298 :
6299 0 : MaybeComma comma = NO_COMMA;
6300 0 : for (unsigned i = 0; i < numCounts; i++) {
6301 0 : double value = counts.get(i);
6302 0 : if (value > 0) {
6303 0 : AppendJSONProperty(buf, OpcodeCounts::countName(op, i), comma);
6304 0 : comma = COMMA;
6305 0 : NumberValueToStringBuffer(cx, DoubleValue(value), buf);
6306 : }
6307 : }
6308 :
6309 0 : buf.append('}');
6310 0 : buf.append('}');
6311 : }
6312 :
6313 0 : buf.append(']');
6314 0 : buf.append('}');
6315 :
6316 0 : return !cx->isExceptionPending();
6317 : }
6318 :
6319 : JS_FRIEND_API(JSString *)
6320 0 : GetPCCountScriptContents(JSContext *cx, size_t index)
6321 : {
6322 0 : JSRuntime *rt = cx->runtime;
6323 :
6324 0 : if (!rt->scriptPCCounters || index >= rt->scriptPCCounters->length()) {
6325 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BUFFER_TOO_SMALL);
6326 0 : return NULL;
6327 : }
6328 :
6329 0 : const ScriptOpcodeCountsPair &info = (*rt->scriptPCCounters)[index];
6330 0 : JSScript *script = info.script;
6331 :
6332 0 : StringBuffer buf(cx);
6333 :
6334 0 : if (!script->function() && !script->compileAndGo)
6335 0 : return buf.finishString();
6336 :
6337 : {
6338 0 : JSAutoEnterCompartment ac;
6339 0 : if (!ac.enter(cx, script->function() ? (JSObject *) script->function() : script->global()))
6340 0 : return NULL;
6341 :
6342 0 : if (!GetPCCountJSON(cx, info, buf))
6343 0 : return NULL;
6344 : }
6345 :
6346 0 : return buf.finishString();
6347 : }
6348 :
6349 59610 : } // namespace js
|