From ea579e04967ff61aaad616c3123f8c3500378c44 Mon Sep 17 00:00:00 2001 From: "mccabe%netscape.com" Date: Wed, 8 Nov 2000 20:39:43 +0000 Subject: [PATCH] Fix to 58946. Make try { ... } catch(exn) { return exn } work by ensuring that the return value (exn) is maintained on the stack as we pop off scopes to return from the try/catch/finally. The newly added JSOP_SWAP opcode helps us bubble. This fixes a regression uncovered by the fix to 56716. (I've noticed that this causes *depend* builds of the standalone JS shell to crash on this construct, but I've tested in the Mozilla build, and the dependencies seem to solve the problem there.) r=brendan.mozilla.org sr=jband@netscape.com --- js/src/jsemit.c | 41 +++++++++++++++++++++++++++++++++-------- js/src/jsinterp.c | 12 ++++++++++++ js/src/jsopcode.tbl | 8 ++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/js/src/jsemit.c b/js/src/jsemit.c index 81b6fa73f475..5404c891a38b 100644 --- a/js/src/jsemit.c +++ b/js/src/jsemit.c @@ -306,11 +306,16 @@ 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) +EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, + JSBool preserveTop) { JSStmtInfo *stmt; ptrdiff_t jmp; + /* + * If we're here as part of processing a return, emit JSOP_SWAP to preserve + * the top element. + */ for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { switch (stmt->type) { case STMT_FINALLY: @@ -322,6 +327,10 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt) break; case STMT_WITH: case STMT_CATCH: + if (preserveTop) { + if (js_Emit1(cx, cg, JSOP_SWAP) < 0) + return JS_FALSE; + } if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) return JS_FALSE; cg->stackDepth++; @@ -329,11 +338,21 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt) return JS_FALSE; break; case STMT_FOR_IN_LOOP: - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; cg->stackDepth += 2; - if (js_Emit1(cx, cg, JSOP_POP2) < 0) - return JS_FALSE; + 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. */ + if (js_Emit1(cx, cg, JSOP_POP2) < 0) + return JS_FALSE; + } break; default:; } @@ -349,7 +368,7 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, intN index; ptrdiff_t jmp; - if (!EmitNonLocalJumpFixup(cx, cg, toStmt)) + if (!EmitNonLocalJumpFixup(cx, cg, toStmt, JS_FALSE)) return -1; if (label) { @@ -1822,8 +1841,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) break; case TOK_RETURN: - if (!EmitNonLocalJumpFixup(cx, cg, NULL)) - return JS_FALSE; + /* Push a return value */ pn2 = pn->pn_kid; if (pn2) { if (!js_EmitTree(cx, cg, pn2)) @@ -1832,6 +1850,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_Emit1(cx, cg, JSOP_PUSH) < 0) return JS_FALSE; } + + /* + * EmitNonLocalJumpFixup emits JSOP_SWAPs to maintain the return value + * at the top of the stack, so the return still executes OK. + */ + if (!EmitNonLocalJumpFixup(cx, cg, NULL, JS_TRUE)) + return JS_FALSE; if (js_Emit1(cx, cg, JSOP_RETURN) < 0) return JS_FALSE; break; diff --git a/js/src/jsinterp.c b/js/src/jsinterp.c index ea25d2c906e2..8db02b05fb3a 100644 --- a/js/src/jsinterp.c +++ b/js/src/jsinterp.c @@ -1322,6 +1322,18 @@ js_Interpret(JSContext *cx, jsval *result) sp -= 2; break; + case JSOP_SWAP: + /* + * N.B. JSOP_SWAP doesn't swap the corresponding pc stack + * generating pcs, as they're not needed for the current use of + * preserving the top-of-stack return value when popping scopes + * while returning from catch blocks. + */ + ltmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = ltmp; + break; + case JSOP_POPV: *result = POP_OPND(); break; diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 9ebf55650499..f576afecb740 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -281,3 +281,11 @@ OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 11, JOF_UINT16 */ OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE) + +/* + * Swap the top two stack elements. + * N.B. JSOP_SWAP doesn't swap the corresponding pc stack generating pcs, as + * they're not needed for the current use of preserving the top-of-stack return + * value when popping scopes while returning from catch blocks. + */ +OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE)