Handle extended indexes around JSOP_*BLOCKCHAIN (610026, r=billm).

This commit is contained in:
Brendan Eich 2010-11-05 15:03:39 -07:00
parent 44a3f3c372
commit fdf9d2fd7c
9 changed files with 126 additions and 65 deletions

View File

@ -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

View File

@ -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 = &regs.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);
/*

View File

@ -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;

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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");