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