Commit 7b2b1b44 authored by brendan%mozilla.org's avatar brendan%mozilla.org
Browse files

Fix try/finally and try/guarded-catches/finally hard cases (104077, r=jband, sr=shaver).

parent cb9ae928
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -997,7 +997,7 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
            JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
            if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
                uint8 flags = fun->flags;
                fputs("flags:", stderr);
                fputs("flags:", stdout);

#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);

@@ -1126,7 +1126,7 @@ Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
                JS_GetStringBytes(str));
        return JS_TRUE;
    }
    cx->tracefp = bval ? stdout : NULL;
    cx->tracefp = bval ? stderr : NULL;
    return JS_TRUE;
}

+144 −69
Original line number Diff line number Diff line
@@ -1189,15 +1189,55 @@ js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
/* Emit additional bytecode(s) for non-local jumps. */
static JSBool
EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
                      JSBool preserveTop)
                      JSOp *returnop)
{
    intN depth;
    JSStmtInfo *stmt;
    ptrdiff_t jmp;

    /*
     * If we're here as part of processing a return, emit JSOP_SWAP to preserve
     * the top element.
     * Return from within a try block that has a finally clause must be split
     * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval;
     * and JSOP_RETRVAL, which makes control flow go back to the caller, who
     * picks up fp->rval as usual.  Otherwise, the stack will be unbalanced
     * when executing the finally clause.
     *
     * We mutate *returnop once only if we find an enclosing try-block (viz,
     * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one
     * or more JSOP_GOSUBs and other fixup opcodes emitted by this function.
     * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop.
     * The fixup opcodes and gosubs must interleave in the proper order, from
     * inner statement to outer, so that finally clauses run at the correct
     * stack depth.
     */
    if (returnop) {
        JS_ASSERT(*returnop == JSOP_RETURN);
        for (stmt = cg->treeContext.topStmt; stmt != toStmt;
             stmt = stmt->down) {
            if (stmt->type == STMT_FINALLY) {
                if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
                    return JS_FALSE;
                *returnop = JSOP_RETRVAL;
                break;
            }
        }

        /*
         * If there are no try-with-finally blocks open around this return
         * statement, we can generate a return forthwith and skip generating
         * any fixup code.
         */
        if (*returnop == JSOP_RETURN)
            return JS_TRUE;
    }

    /*
     * The non-local jump fixup we emit will unbalance cg->stackDepth, because
     * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
     * end of a with statement, so we save cg->stackDepth here and restore it
     * just before a successful return.
     */
    depth = cg->stackDepth;
    for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
        switch (stmt->type) {
          case STMT_FINALLY:
@@ -1207,39 +1247,38 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
            if (jmp < 0)
                return JS_FALSE;
            break;

          case STMT_WITH:
          case STMT_CATCH:
            if (preserveTop) {
                if (js_Emit1(cx, cg, JSOP_SWAP) < 0)
                    return JS_FALSE;
            }
            /* There's a With object on the stack that we need to pop. */
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
                return JS_FALSE;
            cg->stackDepth++;
            if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
                return JS_FALSE;
            break;

          case STMT_FOR_IN_LOOP:
            cg->stackDepth += 2;
            if (preserveTop) {
                if (js_Emit1(cx, cg, JSOP_SWAP) < 0 ||
                    js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                    js_Emit1(cx, cg, JSOP_POP) < 0 ||
                    js_Emit1(cx, cg, JSOP_SWAP) < 0 ||
                    js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                    js_Emit1(cx, cg, JSOP_POP) < 0) {
                    return JS_FALSE;
                }
            } else {
                /* JSOP_POP2 isn't decompiled, and doesn't need a src note. */
            /*
             * The iterator and the object being iterated need to be popped.
             * JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN.
             */
            if (js_Emit1(cx, cg, JSOP_POP2) < 0)
                return JS_FALSE;
            }
            break;

          case STMT_SUBROUTINE:
            /* There's a retsub pc-offset on the stack that we need to pop. */
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_POP) < 0)
                return JS_FALSE;
            break;

          default:;
        }
    }

    cg->stackDepth = depth;
    return JS_TRUE;
}

@@ -1250,7 +1289,7 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
    intN index;
    ptrdiff_t jmp;

    if (!EmitNonLocalJumpFixup(cx, cg, toStmt, JS_FALSE))
    if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
        return -1;

    if (label) {
@@ -1268,8 +1307,8 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
}

static JSBool
BackPatch(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
          ptrdiff_t last, jsbytecode *target, jsbytecode op)
BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
          jsbytecode *target, jsbytecode op)
{
    jsbytecode *pc;
    ptrdiff_t delta, span;
@@ -1317,8 +1356,8 @@ js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
    JSStmtInfo *stmt;

    stmt = cg->treeContext.topStmt;
    if (!BackPatch(cx, cg, stmt, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
        !BackPatch(cx, cg, stmt, stmt->continues, CG_CODE(cg, stmt->update),
    if (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
        !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update),
                   JSOP_GOTO)) {
        return JS_FALSE;
    }
@@ -2693,10 +2732,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_EXCEPTIONS

      case TOK_TRY: {
        ptrdiff_t start, end;
        ptrdiff_t catchStart = -1, finallyCatch = -1, catchjmp = -1;
        ptrdiff_t start, end, catchStart, finallyCatch, catchJump;
        JSParseNode *iter;
        uint16 depth;
        intN depth;

        /* Quell GCC overwarnings. */
        end = catchStart = finallyCatch = catchJump = -1;

/* Emit JSOP_GOTO that points to the first op after the catch/finally blocks */
#define EMIT_CATCH_GOTO(cx, cg, jmp)                                          \
@@ -2720,10 +2761,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
                         CG_OFFSET(cg));

        /*
         * About JSOP_SETSP:
         * An exception can be thrown while the stack is in an unbalanced
         * state, and this causes problems with things like function invocation
         * later on.
         * About JSOP_SETSP: an exception can be thrown while the stack is in
         * an unbalanced state, and this imbalance causes problems with things
         * like function invocation later on.
         *
         * To fix this, we compute the `balanced' stack depth upon try entry,
         * and then restore the stack to this depth when we hit the first catch
@@ -2740,7 +2780,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
        if (!js_EmitTree(cx, cg, pn->pn_kid1))
            return JS_FALSE;

        /* Emit (hidden) jump over catch and/or finally. */
        /* GOSUB to finally, if present. */
        if (pn->pn_kid3) {
            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
                return JS_FALSE;
@@ -2749,6 +2789,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
                return JS_FALSE;
        }

        /* Emit (hidden) jump over catch and/or finally. */
        if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
            return JS_FALSE;
        EMIT_CATCH_GOTO(cx, cg, jmp);
@@ -2792,11 +2833,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
                if (!UpdateLinenoNotes(cx, cg, iter))
                    return JS_FALSE;

                if (catchjmp != -1) {
                if (catchJump != -1) {
                    /* Fix up and clean up previous catch block. */
                    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
                    if ((uintN)++cg->stackDepth > cg->maxStackDepth)
                        cg->maxStackDepth = cg->stackDepth;
                    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump);

                    /* Compensate for the [leavewith]. */
                    cg->stackDepth++;
                    JS_ASSERT(cg->stackDepth <= cg->maxStackDepth);

                    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                        js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
                        return JS_FALSE;
@@ -2804,6 +2848,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
                } else {
                    /* Set stack to original depth (see SETSP comment above). */
                    EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);
                    cg->stackDepth = depth;
                }

                /* Non-negative guardnote offset is length of catchguard. */
@@ -2848,8 +2893,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
                        return JS_FALSE;
                    }
                    /* ifeq <next block> */
                    catchjmp = EmitJump(cx, cg, JSOP_IFEQ, 0);
                    if (catchjmp < 0)
                    catchJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
                    if (catchJump < 0)
                        return JS_FALSE;
                }

@@ -2897,38 +2942,46 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
         * code while letting an uncaught exception pass through.
         */
        if (pn->pn_kid3 ||
            (catchjmp != -1 && iter->pn_kid1->pn_expr)) {
            (catchJump != -1 && iter->pn_kid1->pn_expr)) {
            /*
             * Emit another stack fix, because the catch could itself
             * Emit another stack fixup, because the catch could itself
             * throw an exception in an unbalanced state, and the finally
             * may need to call functions etc.
             * may need to call functions.  If there is no finally, only
             * guarded catches, the rethrow code below nevertheless needs
             * stack fixup.
             */
            finallyCatch = CG_OFFSET(cg);
            EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);

            if (catchjmp != -1 && iter->pn_kid1->pn_expr)
                CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
            cg->stackDepth = depth;

            /* Last discriminant jumps to rethrow if none match. */
            if ((uintN)++cg->stackDepth > cg->maxStackDepth)
                cg->maxStackDepth = cg->stackDepth;
            if (pn->pn_kid2 &&
                (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                 js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)) {
            if (catchJump != -1 && iter->pn_kid1->pn_expr) {
                CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump);

                /* Compensate for the [leavewith]. */
                cg->stackDepth++;
                JS_ASSERT(cg->stackDepth <= cg->maxStackDepth);

                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                    js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
                    return JS_FALSE;
                }
            }

            if (pn->pn_kid3) {
                finallyCatch = CG_OFFSET(cg);
                EMIT_FINALLY_GOSUB(cx, cg, jmp);
                if (jmp < 0)
                    return JS_FALSE;
                cg->stackDepth = depth;
            }

            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 ||
                js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
                js_Emit1(cx, cg, JSOP_THROW) < 0) {
                return JS_FALSE;
            }
            JS_ASSERT(cg->stackDepth == depth);
        }

        /*
@@ -2936,11 +2989,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
         * gosubs that might have been emitted before non-local jumps.
         */
        if (pn->pn_kid3) {
            if (!BackPatch(cx, cg, &stmtInfo, stmtInfo.gosub, CG_NEXT(cg),
                           JSOP_GOSUB)) {
            if (!BackPatch(cx, cg, stmtInfo.gosub, CG_NEXT(cg), JSOP_GOSUB))
                return JS_FALSE;
            }
            js_PopStatementCG(cx, cg);

            /*
             * The stack budget must be balanced at this point, and we need
             * one more slot for the JSOP_RETSUB return address pushed by a
             * JSOP_GOSUB opcode that calls this finally clause.
             */
            JS_ASSERT(cg->stackDepth == depth);
            if ((uintN)++cg->stackDepth > cg->maxStackDepth)
                cg->maxStackDepth = cg->stackDepth;

            /* Now indicate that we're emitting a subroutine body. */
            stmtInfo.type = STMT_SUBROUTINE;
            if (!UpdateLinenoNotes(cx, cg, pn->pn_kid3))
                return JS_FALSE;
            if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||
@@ -2948,9 +3010,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
                js_Emit1(cx, cg, JSOP_RETSUB) < 0) {
                return JS_FALSE;
            }
        } else {
            js_PopStatementCG(cx, cg);
        }
        js_PopStatementCG(cx, cg);

        if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
            js_Emit1(cx, cg, JSOP_NOP) < 0) {
@@ -2958,17 +3019,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
        }

        /* Fix up the end-of-try/catch jumps to come here. */
        if (!BackPatch(cx, cg, &stmtInfo, stmtInfo.catchJump, CG_NEXT(cg),
                       JSOP_GOTO)) {
        if (!BackPatch(cx, cg, stmtInfo.catchJump, CG_NEXT(cg), JSOP_GOTO))
            return JS_FALSE;
        }

        /*
         * Add the try note last, to let post-order give us the right ordering
         * (first to last for a given nesting level, inner to outer by level).
         */
        if (pn->pn_kid2) {
            JS_ASSERT(catchStart != -1);
            JS_ASSERT(end != -1 && catchStart != -1);
            if (!js_NewTryNote(cx, cg, start, end, catchStart))
                return JS_FALSE;
        }
@@ -3054,12 +3113,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
        }

        /*
         * EmitNonLocalJumpFixup emits JSOP_SWAPs to maintain the return value
         * at the top of the stack, so the return still executes OK.
         * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
         * JSOP_SETRVAL if there are open try blocks having finally clauses.
         * We can't simply transfer control flow to our caller in that case,
         * because we must gosub to those clauses from inner to outer, with
         * the correct stack pointer (i.e., after popping any with, for/in,
         * etc., slots nested inside the finally's try).
         */
        if (!EmitNonLocalJumpFixup(cx, cg, NULL, JS_TRUE))
        op = JSOP_RETURN;
        if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
            return JS_FALSE;
        if (js_Emit1(cx, cg, JSOP_RETURN) < 0)
        if (js_Emit1(cx, cg, op) < 0)
            return JS_FALSE;
        break;

@@ -3307,6 +3371,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
        if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
            return JS_FALSE;

        /*
         * Because each branch pushes a single value, but our stack budgeting
         * analysis ignores branches, we now have two values accounted for in
         * cg->stackDepth.  Execution will follow only one path, so we must
         * decrement cg->stackDepth here.  Failing to do this will foil code,
         * such as the try/catch/finally exception handling code generator,
         * that samples cg->stackDepth for use at runtime (JSOP_SETSP).
         */
        JS_ASSERT(cg->stackDepth > 1);
        cg->stackDepth--;
        break;

      case TOK_OR:
+3 −3
Original line number Diff line number Diff line
@@ -60,7 +60,8 @@ typedef enum JSStmtType {
    STMT_DO_LOOP      = 9,      /* do/while loop statement */
    STMT_FOR_LOOP     = 10,     /* for loop statement */
    STMT_FOR_IN_LOOP  = 11,     /* for/in loop statement */
    STMT_WHILE_LOOP   = 12      /* while loop statement */
    STMT_WHILE_LOOP   = 12,     /* while loop statement */
    STMT_SUBROUTINE   = 13      /* gosub-target subroutine body */
} JSStmtType;

#define STMT_IS_LOOP(stmt)      ((stmt)->type >= STMT_DO_LOOP)
@@ -69,7 +70,6 @@ typedef struct JSStmtInfo JSStmtInfo;

struct JSStmtInfo {
    JSStmtType      type;           /* statement type */
    ptrdiff_t       top;            /* offset of loop top from cg base */
    ptrdiff_t       update;         /* loop update offset (top if none) */
    ptrdiff_t       breaks;         /* offset of last break in loop */
    ptrdiff_t       continues;      /* offset of last continue in loop */
@@ -80,7 +80,7 @@ struct JSStmtInfo {
};

#define SET_STATEMENT_TOP(stmt, top)                                          \
    ((stmt)->top = (stmt)->update = (top), (stmt)->breaks =                   \
    ((stmt)->update = (top), (stmt)->breaks =                                 \
     (stmt)->continues = (stmt)->catchJump = (stmt)->gosub = (-1))

struct JSTreeContext {              /* tree context for semantic checks */
+17 −2
Original line number Diff line number Diff line
@@ -1334,7 +1334,7 @@ js_Interpret(JSContext *cx, jsval *result)
                                JS_GetStringBytes(str));
                    }
                }
                putc('\n', tracefp);
                fprintf(tracefp, " @ %d\n", sp - fp->spbase);
            }
        }
#endif
@@ -1422,9 +1422,16 @@ js_Interpret(JSContext *cx, jsval *result)
            fp->scopeChain = JSVAL_TO_OBJECT(rval);
            break;

          case JSOP_SETRVAL:
            fp->rval = POP_OPND();
            break;

          case JSOP_RETURN:
            CHECK_BRANCH(-1);
            fp->rval = POP_OPND();
            /* FALL THROUGH */

          case JSOP_RETRVAL:    /* fp->rval already set */
            if (inlineCallCount)
          inline_return:
            {
@@ -3876,6 +3883,7 @@ js_Interpret(JSContext *cx, jsval *result)
#ifdef DEBUG
        if (tracefp) {
            intN ndefs, n;
            jsval *siter;

            ndefs = cs->ndefs;
            if (ndefs) {
@@ -3888,8 +3896,15 @@ js_Interpret(JSContext *cx, jsval *result)
                                JS_GetStringBytes(str));
                    }
                }
                putc('\n', tracefp);
                fprintf(tracefp, " @ %d\n", sp - fp->spbase);
            }
            fprintf(tracefp, "  stack: ");
            for (siter = fp->spbase; siter < sp; siter++) {
                str = js_ValueToSource(cx, *siter);
                fprintf(tracefp, "%s ",
                        str ? JS_GetStringBytes(str) : "<null>");
            }
            fputc('\n', tracefp);
        }
#endif
    }
+74 −24
Original line number Diff line number Diff line
@@ -927,7 +927,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
		switch (sn ? SN_TYPE(sn) : SRC_NULL) {
#if JS_HAS_DO_WHILE_LOOP
		  case SRC_WHILE:
		    js_printf(jp, "\tdo {\n");	/* balance} */
		    js_printf(jp, "\tdo {\n");
		    jp->indent += 4;
		    break;
#endif /* JS_HAS_DO_WHILE_LOOP */
@@ -999,7 +999,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
		    jp->indent -= 4;
		    sn = js_GetSrcNote(jp->script, pc);
		    pc += oplen;
		    js_printf(jp, "\t} catch ("); /* balance) */
		    js_printf(jp, "\t} catch (");

                    LOCAL_ASSERT(*pc == JSOP_NAME);
                    pc += js_CodeSpec[JSOP_NAME].length;
@@ -1026,7 +1026,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                        pc += js_CodeSpec[*pc].length;
		    }

		    js_printf(jp, ") {\n"); /* balance} */
		    js_printf(jp, ") {\n");
		    jp->indent += 4;
		    len = 0;
		    break;
@@ -1053,6 +1053,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)

		  default:;
		}
              case JSOP_RETRVAL:
		break;

              case JSOP_GROUP:
@@ -1078,24 +1079,67 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
              case JSOP_TRY:
                js_printf(jp, "\ttry {\n");
                jp->indent += 4;
		todo = -2;
                break;

            {
              static const char finally_cookie[] = "finally-cookie";

              case JSOP_FINALLY:
                jp->indent -= 4;
                js_printf(jp, "\t} finally {\n");
                jp->indent += 4;

                /*
                 * We must push an empty string placeholder for gosub's return
                 * address, popped by JSOP_RETSUB and counted by script->depth
                 * but not by ss->top (see JSOP_SETSP, below).
                 */
                todo = Sprint(&ss->sprinter, finally_cookie);
                break;

              case JSOP_RETSUB:
                rval = POP_STR();
                LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0);
                todo = -2;
                break;
            }

              case JSOP_SWAP:
                /*
                 * We don't generate this opcode currently, and previously we
                 * did not need to decompile it.  If old, serialized bytecode
                 * uses it still, we should fall through and set todo = -2.
                 */
                /* FALL THROUGH */

	      case JSOP_GOSUB:
	      case JSOP_GOSUBX:
	      case JSOP_RETSUB:
	      case JSOP_SETSP:
                /*
                 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
                 * string stack because the next op in bytecode order finds
                 * the stack balanced by a JSOP_RETSUB executed elsewhere.
                 */
		todo = -2;
		break;

	      case JSOP_SETSP:
                /*
                 * The compiler models operand stack depth and fixes the stack
                 * pointer on entry to a catch clause based on its depth model.
                 * The decompiler must match the code generator's model, which
                 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
                 */
                ss->top = (uintN) GET_ATOM_INDEX(pc);
		break;

	      case JSOP_EXCEPTION:
                /*
                 * The only other JSOP_EXCEPTION case occurs as part of a code
                 * sequence that follows a SRC_CATCH-annotated JSOP_NOP.
                 */
                sn = js_GetSrcNote(jp->script, pc);
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
                LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
                todo = -2;
		break;
#endif /* JS_HAS_EXCEPTIONS */
@@ -1154,14 +1198,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
		todo = -2;
		break;

            {
              static const char with_cookie[] = "with-cookie";

	      case JSOP_ENTERWITH:
		sn = js_GetSrcNote(jp->script, pc);
		if (sn && SN_TYPE(sn) == SRC_HIDDEN) {
                    todo = -2;
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
                    break;
                }
                rval = POP_STR();
                js_printf(jp, "\twith (%s) {\n", rval);
                jp->indent += 4;
                todo = Sprint(&ss->sprinter, with_cookie);
		break;

	      case JSOP_LEAVEWITH:
@@ -1169,10 +1218,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
                todo = -2;
		if (sn && SN_TYPE(sn) == SRC_HIDDEN)
                    break;
                rval = POP_STR();
                LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
                jp->indent -= 4;
                js_printf(jp, "\t}\n");
		break;
            }

              case JSOP_SETRVAL:
	      case JSOP_RETURN:
		rval = POP_STR();
		if (*rval != '\0')
@@ -1293,7 +1346,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
#if JS_HAS_DO_WHILE_LOOP
		/* Currently, this must be a do-while loop's upward branch. */
                jp->indent -= 4;
                /* {balance: */
                js_printf(jp, "\t} while (%s);\n", POP_STR());
                todo = -2;
#else
@@ -1433,12 +1485,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
	      case JSOP_DUP2:
		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
		todo = SprintPut(&ss->sprinter, rval, strlen(rval));
		if (todo < 0 || !PushOff(ss, todo, op))
		if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
		    return JS_FALSE;
		/* FALL THROUGH */

	      case JSOP_DUP:
		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
                op = ss->opcodes[ss->top-1];
		todo = SprintPut(&ss->sprinter, rval, strlen(rval));
		break;

@@ -2073,12 +2126,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
		    todo = Sprint(&ss->sprinter, "#%u=%c",
				  (unsigned) i,
				  (*lval == 'O') ? '{' : '[');
		    /* balance}] */
		} else
#endif /* JS_HAS_SHARP_VARS */
		{
		    todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
		    /* balance}] */
		}
		break;

@@ -2088,7 +2139,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
		todo = Sprint(&ss->sprinter, "%s%s%c",
			      rval,
			      (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
			      /* [balance */
			      (*rval == '{') ? '}' : ']');
		break;

Loading