diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index ff4fead0aa6..af8c5adf9dc 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -1529,8 +1529,7 @@ EmitKnownBlockChain(JSContext *cx, JSCodeGenerator *cg, JSObjectBox *box) { if (box) return EmitIndexOp(cx, JSOP_BLOCKCHAIN, box->index, cg); - else - return js_Emit1(cx, cg, JSOP_NULLBLOCKCHAIN) >= 0; + return js_Emit1(cx, cg, JSOP_NULLBLOCKCHAIN) >= 0; } static JSBool diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index e2f02d9b3c0..bddd10a5147 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -202,36 +202,37 @@ js::GetBlockChain(JSContext *cx, JSStackFrame *fp) if (!fp->isScriptFrame()) return NULL; + /* Assume that imacros don't affect blockChain */ + jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); + JSScript *script = fp->script(); jsbytecode *start = script->code; - /* Assume that imacros don't affect blockChain */ - jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx); - - JS_ASSERT(pc >= start && pc < script->code + script->length); + JS_ASSERT(target >= start && target < start + script->length); JSObject *blockChain = NULL; - if (*pc == JSOP_BLOCKCHAIN) { - blockChain = script->getObject(GET_INDEX(pc)); - } else if (*pc == JSOP_NULLBLOCKCHAIN) { - blockChain = NULL; - } else { - ptrdiff_t oplen; - for (jsbytecode *p = start; p < pc; p += oplen) { - JSOp op = js_GetOpcode(cx, script, p); - const JSCodeSpec *cs = &js_CodeSpec[op]; - oplen = cs->length; - if (oplen < 0) - oplen = js_GetVariableBytecodeLength(p); + uintN indexBase = 0; + ptrdiff_t oplen; + for (jsbytecode *pc = start; pc < target; pc += oplen) { + JSOp op = js_GetOpcode(cx, script, pc); + const JSCodeSpec *cs = &js_CodeSpec[op]; + oplen = cs->length; + if (oplen < 0) + oplen = js_GetVariableBytecodeLength(pc); - if (op == JSOP_ENTERBLOCK) - blockChain = script->getObject(GET_INDEX(p)); - else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) - blockChain = blockChain->getParent(); - else if (op == JSOP_BLOCKCHAIN) - blockChain = script->getObject(GET_INDEX(p)); - else if (op == JSOP_NULLBLOCKCHAIN) - blockChain = NULL; - } + if (op == JSOP_INDEXBASE) + indexBase = GET_INDEXBASE(pc); + else if (op == JSOP_INDEXBASE1 || op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3) + indexBase = (op - JSOP_INDEXBASE1 + 1) << 16; + else if (op == JSOP_RESETBASE || op == JSOP_RESETBASE0) + indexBase = 0; + else if (op == JSOP_ENTERBLOCK) + blockChain = script->getObject(indexBase + GET_INDEX(pc)); + else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) + blockChain = blockChain->getParent(); + else if (op == JSOP_BLOCKCHAIN) + blockChain = script->getObject(indexBase + GET_INDEX(pc)); + else if (op == JSOP_NULLBLOCKCHAIN) + blockChain = NULL; } return blockChain; @@ -249,29 +250,19 @@ js::GetBlockChainFast(JSContext *cx, JSStackFrame *fp, JSOp op, size_t oplen) { /* Assume that we're in a script frame. */ jsbytecode *pc = fp->pc(cx); + JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op); - /* The fast path. */ - if (pc[oplen] == JSOP_NULLBLOCKCHAIN) { - JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op); + pc += oplen; + op = JSOp(*pc); + JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op); + + /* The fast paths assume no JSOP_RESETBASE/INDEXBASE noise. */ + if (op == JSOP_NULLBLOCKCHAIN) return NULL; - } + if (op == JSOP_BLOCKCHAIN) + return fp->script()->getObject(GET_INDEX(pc)); - JSScript *script = fp->script(); - - JS_ASSERT(js_GetOpcode(cx, script, pc) == op); - - JSObject *blockChain; - JSOp opNext = js_GetOpcode(cx, script, pc + oplen); - if (opNext == JSOP_BLOCKCHAIN) { - blockChain = script->getObject(GET_INDEX(pc + oplen)); - } else if (opNext == JSOP_NULLBLOCKCHAIN) { - blockChain = NULL; - } else { - blockChain = NULL; /* appease gcc */ - JS_NOT_REACHED("invalid opcode for fast block chain access"); - } - - return blockChain; + return GetBlockChain(cx, fp); } /* @@ -5639,7 +5630,7 @@ BEGIN_CASE(JSOP_LAMBDA) parent = ®s.fp->scopeChain(); if (obj->getParent() == parent) { - jsbytecode *pc2 = js_AdvanceOverBlockchain(regs.pc + JSOP_LAMBDA_LENGTH); + jsbytecode *pc2 = AdvanceOverBlockchainOp(regs.pc + JSOP_LAMBDA_LENGTH); JSOp op2 = JSOp(*pc2); /* diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 51adbc97de2..4245d90b67e 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -4109,10 +4109,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) pc += len; if (*pc == JSOP_BLOCKCHAIN) { pc += JSOP_BLOCKCHAIN_LENGTH; - } else if (*pc == JSOP_NULLBLOCKCHAIN) { - pc += JSOP_NULLBLOCKCHAIN_LENGTH; } else { - JS_NOT_REACHED("should see block chain operation"); + LOCAL_ASSERT(*pc == JSOP_NULLBLOCKCHAIN); + pc += JSOP_NULLBLOCKCHAIN_LENGTH; } LOCAL_ASSERT(*pc == JSOP_PUSH); pc += JSOP_PUSH_LENGTH; diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 7b88ba61a67..89360a4a25f 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -455,7 +455,7 @@ OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24 * JSOP_INDEXBASE and JSOP_RESETBASE to provide the upper bits of the index. * See jsemit.c, EmitIndexOp. */ -OPDEF(JSOP_INDEXBASE, 189,"atombase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE, 189,"indexbase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE) OPDEF(JSOP_RESETBASE, 190,"resetbase", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_RESETBASE0, 191,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE) @@ -532,9 +532,9 @@ OPDEF(JSOP_GETLOCALPROP, 211,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTAT * Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after * the opcode that they prefix. */ -OPDEF(JSOP_INDEXBASE1, 212,"atombase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) -OPDEF(JSOP_INDEXBASE2, 213,"atombase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) -OPDEF(JSOP_INDEXBASE3, 214,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE1, 212,"indexbase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE2, 213,"indexbase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) +OPDEF(JSOP_INDEXBASE3, 214,"indexbase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) OPDEF(JSOP_CALLGNAME, 215, "callgname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP|JOF_GNAME) OPDEF(JSOP_CALLLOCAL, 216, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP) diff --git a/js/src/jsopcodeinlines.h b/js/src/jsopcodeinlines.h index 8e2556642bd..0eb05d587c4 100644 --- a/js/src/jsopcodeinlines.h +++ b/js/src/jsopcodeinlines.h @@ -13,12 +13,11 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. + * The Original Code is the Mozilla SpiderMonkey JaegerMonkey implementation * * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2002-2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): @@ -39,13 +38,20 @@ #include "jsautooplen.h" +namespace js { + +/* + * Warning: this does not skip JSOP_RESETBASE* or JSOP_INDEXBASE* ops, so it is + * useful only when checking for optimization opportunities. + */ JS_ALWAYS_INLINE jsbytecode * -js_AdvanceOverBlockchain(jsbytecode *pc) +AdvanceOverBlockchainOp(jsbytecode *pc) { if (*pc == JSOP_NULLBLOCKCHAIN) return pc + JSOP_NULLBLOCKCHAIN_LENGTH; - else if (*pc == JSOP_BLOCKCHAIN) + if (*pc == JSOP_BLOCKCHAIN) return pc + JSOP_BLOCKCHAIN_LENGTH; - else - return pc; + return pc; +} + } diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index fa18ac06a7c..77bef923a80 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -14847,7 +14847,7 @@ TraceRecorder::record_JSOP_LAMBDA() if (FUN_OBJECT(fun)->getParent() != globalObj) RETURN_STOP_A("Null closure function object parent must be global object"); - jsbytecode *pc2 = js_AdvanceOverBlockchain(cx->regs->pc + JSOP_LAMBDA_LENGTH); + jsbytecode *pc2 = AdvanceOverBlockchainOp(cx->regs->pc + JSOP_LAMBDA_LENGTH); JSOp op2 = JSOp(*pc2); if (op2 == JSOP_INITMETHOD) { diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 375b97aca72..9dd19fc5c7a 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1719,7 +1719,7 @@ mjit::Compiler::generateMethod() JSObjStubFun stub = stubs::Lambda; uint32 uses = 0; - jsbytecode *pc2 = js_AdvanceOverBlockchain(PC + JSOP_LAMBDA_LENGTH); + jsbytecode *pc2 = AdvanceOverBlockchainOp(PC + JSOP_LAMBDA_LENGTH); JSOp next = JSOp(*pc2); if (next == JSOP_INITMETHOD) { diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index 411a6c1fe1a..ef53c13f8fe 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -50,4 +50,5 @@ script regress-600137.js script regress-601399.js script regress-602621.js fails-if(!xulRuntime.shell) script regress-607863.js +script regress-610026.js script regress-609617.js diff --git a/js/src/tests/js1_8_5/regress/regress-610026.js b/js/src/tests/js1_8_5/regress/regress-610026.js new file mode 100644 index 00000000000..3264bf4feef --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-610026.js @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var expect = "pass"; +var actual; + +/* + * We hardcode here that GenerateBlockId limits a program to 2^20 blocks. Start + * with 2^19 blocks, then test 2^20 - 1 blocks, finally test the limit. + */ +var s = "{}"; +for (var i = 0; i < 19; i++) + s += s; + +try { + eval(s); + actual = "pass"; +} catch (e) { + actual = "fail: " + e; +} + +assertEq(actual, expect); + +s += s.slice(0, -2); + +try { + eval(s); + actual = "pass"; +} catch (e) { + actual = "fail: " + e; +} + +assertEq(actual, expect); + +s += "{}"; + +try { + eval(s); + actual = "fail: expected InternalError: program too large"; +} catch (e) { + actual = (e.message == "program too large") ? "pass" : "fail: " + e; +} + +assertEq(actual, expect); + +/* Make 64K blocks in a row, each with two vars, the second one named x. */ +s = "{let y, x;}"; +for (i = 0; i < 16; i++) + s += s; + +/* Now append code to alias block 0 and botch a JS_NOT_REACHED or get the wrong x. */ +s += "var g; { let x = 42; g = function() { return x; }; x = x; }"; + +try { + eval(s); + actual = g(); +} catch (e) { + actual = e; +} +assertEq(actual, 42); + +reportCompare(0, 0, "ok");