Bug 937058 - Paper over debug-mode checks of stack depth for unreachable bytecode. r=jandem

This commit is contained in:
Andy Wingo 2013-11-11 16:21:20 +01:00
parent e3b27bcf78
commit 89a820f764
5 changed files with 88 additions and 63 deletions

View File

@ -0,0 +1,5 @@
// |jit-test| error: ReferenceError
for (var c in foo)
try {
throw new Error();
} catch (e) {}

View File

@ -795,30 +795,31 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
BaselineScript *baselineScript = script->baselineScript();
#ifdef DEBUG
uint32_t expectedDepth = js_ReconstructStackDepth(cx, script,
resumeAfter ? GetNextPc(pc) : pc);
if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) {
if (op == JSOP_FUNCALL) {
// For fun.call(this, ...); the reconstructStackDepth will
// include the this. When inlining that is not included.
// So the exprStackSlots will be one less.
JS_ASSERT(expectedDepth - exprStackSlots <= 1);
} else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
// Accessors coming out of ion are inlined via a complete
// lie perpetrated by the compiler internally. Ion just rearranges
// the stack, and pretends that it looked like a call all along.
// This means that the depth is actually one *more* than expected
// by the interpreter, as there is now a JSFunction, |this| and [arg],
// rather than the expected |this| and [arg]
// Note that none of that was pushed, but it's still reflected
// in exprStackSlots.
JS_ASSERT(exprStackSlots - expectedDepth == 1);
} else {
// For fun.apply({}, arguments) the reconstructStackDepth will
// have stackdepth 4, but it could be that we inlined the
// funapply. In that case exprStackSlots, will have the real
// arguments in the slots and not be 4.
JS_ASSERT(exprStackSlots == expectedDepth);
uint32_t expectedDepth;
if (ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth)) {
if (op != JSOP_FUNAPPLY || !iter.moreFrames() || resumeAfter) {
if (op == JSOP_FUNCALL) {
// For fun.call(this, ...); the reconstructStackDepth will
// include the this. When inlining that is not included.
// So the exprStackSlots will be one less.
JS_ASSERT(expectedDepth - exprStackSlots <= 1);
} else if (iter.moreFrames() && (IsGetPropPC(pc) || IsSetPropPC(pc))) {
// Accessors coming out of ion are inlined via a complete
// lie perpetrated by the compiler internally. Ion just rearranges
// the stack, and pretends that it looked like a call all along.
// This means that the depth is actually one *more* than expected
// by the interpreter, as there is now a JSFunction, |this| and [arg],
// rather than the expected |this| and [arg]
// Note that none of that was pushed, but it's still reflected
// in exprStackSlots.
JS_ASSERT(exprStackSlots - expectedDepth == 1);
} else {
// For fun.apply({}, arguments) the reconstructStackDepth will
// have stackdepth 4, but it could be that we inlined the
// funapply. In that case exprStackSlots, will have the real
// arguments in the slots and not be 4.
JS_ASSERT(exprStackSlots == expectedDepth);
}
}
}

View File

@ -279,25 +279,28 @@ CodeGeneratorShared::encode(LSnapshot *snapshot)
#ifdef DEBUG
if (GetIonContext()->cx) {
uint32_t stackDepth = js_ReconstructStackDepth(GetIonContext()->cx, script, bailPC);
if (JSOp(*bailPC) == JSOP_FUNCALL) {
// For fun.call(this, ...); the reconstructStackDepth will
// include the this. When inlining that is not included.
// So the exprStackSlots will be one less.
JS_ASSERT(stackDepth - exprStack <= 1);
} else if (JSOp(*bailPC) != JSOP_FUNAPPLY &&
!IsGetPropPC(bailPC) && !IsSetPropPC(bailPC))
{
// For fun.apply({}, arguments) the reconstructStackDepth will
// have stackdepth 4, but it could be that we inlined the
// funapply. In that case exprStackSlots, will have the real
// arguments in the slots and not be 4.
uint32_t stackDepth;
if (ReconstructStackDepth(GetIonContext()->cx, script, bailPC, &stackDepth)) {
if (JSOp(*bailPC) == JSOP_FUNCALL) {
// For fun.call(this, ...); the reconstructStackDepth will
// include the this. When inlining that is not included.
// So the exprStackSlots will be one less.
JS_ASSERT(stackDepth - exprStack <= 1);
} else if (JSOp(*bailPC) != JSOP_FUNAPPLY &&
!IsGetPropPC(bailPC) && !IsSetPropPC(bailPC))
{
// For fun.apply({}, arguments) the reconstructStackDepth will
// have stackdepth 4, but it could be that we inlined the
// funapply. In that case exprStackSlots, will have the real
// arguments in the slots and not be 4.
// With accessors, we have different stack depths depending on whether or not we
// inlined the accessor, as the inlined stack contains a callee function that should
// never have been there and we might just be capturing an uneventful property site,
// in which case there won't have been any violence.
JS_ASSERT(exprStack == stackDepth);
// With accessors, we have different stack depths depending on
// whether or not we inlined the accessor, as the inlined stack
// contains a callee function that should never have been there
// and we might just be capturing an uneventful property site, in
// which case there won't have been any violence.
JS_ASSERT(exprStack == stackDepth);
}
}
}
#endif

View File

@ -402,12 +402,16 @@ class BytecodeParser
bool parse();
#ifdef DEBUG
bool isReachable(uint32_t offset) { return maybeCode(offset); }
bool isReachable(const jsbytecode *pc) { return maybeCode(pc); }
#endif
uint32_t stackDepthAtPC(uint32_t offset) {
// Sometimes the code generator in debug mode asks about the stack depth
// of unreachable code (bug 932180 comment 22). Assume that unreachable
// code has no operands on the stack.
Bytecode *code = maybeCode(offset);
return code ? code->stackDepth : 0;
return getCode(offset).stackDepth;
}
uint32_t stackDepthAtPC(const jsbytecode *pc) { return stackDepthAtPC(pc - script_->code); }
@ -697,6 +701,21 @@ BytecodeParser::parse()
#ifdef DEBUG
bool
js::ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc, uint32_t *depth)
{
BytecodeParser parser(cx, script);
if (!parser.parse())
return false;
if (!parser.isReachable(pc))
return false;
*depth = parser.stackDepthAtPC(pc);
return true;
}
/*
* If pc != nullptr, include a prefix indicating whether the PC is at the
* current line. If showAll is true, include the source note type and the
@ -707,10 +726,14 @@ js_DisassembleAtPC(JSContext *cx, JSScript *scriptArg, bool lines,
jsbytecode *pc, bool showAll, Sprinter *sp)
{
RootedScript script(cx, scriptArg);
BytecodeParser parser(cx, script);
jsbytecode *next, *end;
unsigned len;
if (showAll && !parser.parse())
return false;
if (showAll)
Sprint(sp, "%s:%u\n", script->filename(), script->lineno);
@ -757,10 +780,10 @@ js_DisassembleAtPC(JSContext *cx, JSScript *scriptArg, bool lines,
}
else
sp->put(" ");
if (script->hasAnalysis() && script->analysis()->maybeCode(next))
Sprint(sp, "%05u ", script->analysis()->getCode(next).stackDepth);
if (parser.isReachable(next))
Sprint(sp, "%05u ", parser.stackDepthAtPC(next));
else
sp->put(" ");
Sprint(sp, " ", parser.stackDepthAtPC(next));
}
len = js_Disassemble1(cx, script, next, next - script->code, lines, sp);
if (!len)
@ -1985,16 +2008,6 @@ js::DecompileArgument(JSContext *cx, int formalIndex, HandleValue v)
return LossyTwoByteCharsToNewLatin1CharsZ(cx, linear->range()).c_str();
}
unsigned
js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
{
BytecodeParser parser(cx, script);
if (!parser.parse())
return 0;
return parser.stackDepthAtPC(pc);
}
bool
js::CallResultEscapes(jsbytecode *pc)
{

View File

@ -351,14 +351,17 @@ StackUses(JSScript *script, jsbytecode *pc);
extern unsigned
StackDefs(JSScript *script, jsbytecode *pc);
} /* namespace js */
#ifdef DEBUG
/*
* Given bytecode address pc in script's main program code, return the operand
* stack depth just before (JSOp) *pc executes.
* Given bytecode address pc in script's main program code, compute the operand
* stack depth just before (JSOp) *pc executes. If *pc is not reachable, return
* false.
*/
extern unsigned
js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc);
extern bool
ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc, uint32_t *depth);
#endif
} /* namespace js */
#ifdef _MSC_VER
#pragma warning(pop)