Add Script.prototype.getOffsetLine.

This commit is contained in:
Jason Orendorff 2011-06-25 20:58:17 -05:00
parent 1c0e0f089a
commit f87c801c95
8 changed files with 188 additions and 26 deletions

View File

@ -0,0 +1,27 @@
// Basic getOffsetLine test, using Error.lineNumber as the gold standard.
var g = newGlobal('new-compartment');
var dbg = Debug(g);
var hits;
dbg.hooks = {
debuggerHandler: function (frame) {
var knownLine = frame.eval("line").return;
assertEq(frame.script.getOffsetLine(frame.offset), knownLine);
hits++;
}
};
hits = 0;
g.eval("var line = new Error().lineNumber; debugger;");
assertEq(hits, 1);
hits = 0;
g.eval("var s = 2 + 2;\n" +
"s += 2;\n" +
"line = new Error().lineNumber; debugger;\n" +
"s += 2;\n" +
"s += 2;\n" +
"line = new Error().lineNumber; debugger;\n" +
"s += 2;\n" +
"assertEq(s, 12);\n");
assertEq(hits, 2);

View File

@ -0,0 +1,21 @@
// Frame.script/offset and Script.getOffsetLine work in non-top frames.
var g = newGlobal('new-compartment');
var dbg = Debug(g);
var hits = 0;
dbg.hooks = {
debuggerHandler: function (frame) {
var a = [];
for (; frame.type == "call"; frame = frame.older)
a.push(frame.script.getOffsetLine(frame.offset) - g.line0);
assertEq(a.join(","), "1,2,3,4");
hits++;
}
};
g.eval("var line0 = Error().lineNumber;\n" +
"function f0() { debugger; }\n" +
"function f1() { f0(); }\n" +
"function f2() { f1(); }\n" +
"function f3() { f2(); }\n" +
"f3();\n");
assertEq(hits, 1);

View File

@ -0,0 +1,30 @@
// Invalid offsets result in exceptions, not bogus results.
load(libdir + "asserts.js");
var g = newGlobal('new-compartment');
var dbg = Debug(g);
var hits;
dbg.hooks = {
debuggerHandler: function (frame) {
assertEq(frame.script.getOffsetLine(frame.offset), g.line);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(String(frame.offset)); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(Object(frame.offset)); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(-1); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(1000000); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(0.25); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(+Infinity); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(-Infinity); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(NaN); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(false); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(true); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(undefined); }, Error);
assertThrowsInstanceOf(function () { frame.script.getOffsetLine(); }, Error);
hits++;
}
};
hits = 0;
g.eval("var line = new Error().lineNumber; debugger;");

View File

@ -358,3 +358,4 @@ MSG_DEF(JSMSG_DEBUG_OBJECT_WRONG_OWNER, 275, 0, JSEXN_TYPEERR, "Debug.Object bel
MSG_DEF(JSMSG_DEBUG_OBJECT_PROTO, 276, 0, JSEXN_TYPEERR, "Debug.Object.prototype is not a valid Debug.Object")
MSG_DEF(JSMSG_DEBUG_LOOP, 277, 0, JSEXN_TYPEERR, "cannot debug an object in same compartment as debugger or a compartment that is already debugging the debugger")
MSG_DEF(JSMSG_DEBUG_NOT_IDLE, 278, 0, JSEXN_ERR, "can't start debugging: a debuggee script is on the stack")
MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 279, 0, JSEXN_TYPEERR, "invalid script offset")

View File

@ -1275,6 +1275,38 @@ DebugScript_getLive(JSContext *cx, uintN argc, Value *vp)
return true;
}
static bool
ScriptOffset(JSContext *cx, JSScript *script, const Value &v, size_t *offsetp)
{
double d;
size_t off;
bool ok = v.isNumber();
if (ok) {
d = v.toNumber();
off = size_t(d);
}
if (!ok || off != d || !IsValidBytecodeOffset(cx, script, off)) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_BAD_OFFSET);
return false;
}
*offsetp = off;
return true;
}
static JSBool
DebugScript_getOffsetLine(JSContext *cx, uintN argc, Value *vp)
{
REQUIRE_ARGC("Debug.Script.getOffsetLine", 1);
THIS_DEBUGSCRIPT_LIVE_SCRIPT(cx, vp, "getOffsetLine", obj, script);
size_t offset;
if (!ScriptOffset(cx, script, vp[2], &offset))
return false;
uintN lineno = JS_PCToLineNumber(cx, script, script->code + offset);
vp->setNumber(lineno);
return true;
}
static JSBool
DebugScript_construct(JSContext *cx, uintN argc, Value *vp)
{
@ -1288,7 +1320,7 @@ static JSPropertySpec DebugScript_properties[] = {
};
static JSFunctionSpec DebugScript_methods[] = {
// JS_FN("getOffsetLine", DebugScript_getOffsetLine, 0, 0),
JS_FN("getOffsetLine", DebugScript_getOffsetLine, 0, 0),
JS_FS_END
};

View File

@ -72,9 +72,10 @@
#include "jsstaticcheck.h"
#include "jsvector.h"
#include "jsobjinlines.h"
#include "jsscriptinlines.h"
#include "jscntxtinlines.h"
#include "jsobjinlines.h"
#include "jsopcodeinlines.h"
#include "jsscriptinlines.h"
#include "jsautooplen.h"
@ -170,14 +171,12 @@ js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
return base + GET_UINT16(pc + pcoff);
}
uintN
js_GetVariableBytecodeLength(jsbytecode *pc)
size_t
js_GetVariableBytecodeLength(JSOp op, jsbytecode *pc)
{
JSOp op;
uintN jmplen, ncases;
jsint low, high;
op = (JSOp) *pc;
JS_ASSERT(js_CodeSpec[op].length == -1);
switch (op) {
case JSOP_TABLESWITCHX:
@ -5621,4 +5620,27 @@ CallResultEscapes(jsbytecode *pc)
return (*pc != JSOP_IFEQ);
}
size_t
GetBytecodeLength(JSContext *cx, JSScript *script, jsbytecode *pc)
{
JSOp op = js_GetOpcode(cx, script, pc);
JS_ASSERT(op < JSOP_LIMIT);
JS_ASSERT(op != JSOP_TRAP);
if (js_CodeSpec[op].length != -1)
return js_CodeSpec[op].length;
return js_GetVariableBytecodeLength(op, pc);
}
extern bool
IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset)
{
// This could be faster (by following jump instructions if the target is <= offset).
for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
size_t here = r.frontOffset();
if (here >= offset)
return here == offset;
}
return false;
}
} // namespace js

View File

@ -381,12 +381,6 @@ js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
obj = (script)->getRegExp(index_); \
JS_END_MACRO
/*
* Get the length of variable-length bytecode like JSOP_TABLESWITCH.
*/
extern uintN
js_GetVariableBytecodeLength(jsbytecode *pc);
/*
* Find the number of stack slots used by a variadic opcode such as JSOP_CALL
* (for such ops, JSCodeSpec.nuses is -1).
@ -467,10 +461,36 @@ extern char *
js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
JSString *fallback);
/*
* Given bytecode address pc in script's main program code, return the operand
* stack depth just before (JSOp) *pc executes.
*/
extern uintN
js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
JS_END_EXTERN_C
#define JSDVG_IGNORE_STACK 0
#define JSDVG_SEARCH_STACK 1
#ifdef __cplusplus
/*
* Get the length of variable-length bytecode like JSOP_TABLESWITCH.
*/
extern size_t
js_GetVariableBytecodeLength(JSOp op, jsbytecode *pc);
inline size_t
js_GetVariableBytecodeLength(jsbytecode *pc)
{
JS_ASSERT(*pc != JSOP_TRAP);
return js_GetVariableBytecodeLength(JSOp(*pc), pc);
}
namespace js {
static inline char *
@ -519,6 +539,12 @@ Sprint(Sprinter *sp, const char *format, ...);
extern bool
CallResultEscapes(jsbytecode *pc);
extern size_t
GetBytecodeLength(JSContext *cx, JSScript *script, jsbytecode *pc);
extern bool
IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset);
}
#endif
@ -534,17 +560,4 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
JSBool lines, js::Sprinter *sp);
#endif
/*
* Given bytecode address pc in script's main program code, return the operand
* stack depth just before (JSOp) *pc executes.
*/
extern uintN
js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
JS_END_EXTERN_C
#endif /* jsopcode_h___ */

View File

@ -40,6 +40,22 @@
namespace js {
class BytecodeRange {
public:
BytecodeRange(JSContext *cx, JSScript *script)
: cx(cx), script(script), pc(script->code), end(pc + script->length) {}
bool empty() const { return pc == end; }
jsbytecode *frontPC() const { return pc; }
JSOp frontOpcode() const { return js_GetOpcode(cx, script, pc); }
size_t frontOffset() const { return pc - script->code; }
void popFront() { pc += GetBytecodeLength(cx, script, pc); }
private:
JSContext *cx;
JSScript *script;
jsbytecode *pc, *end;
};
/*
* Warning: this does not skip JSOP_RESETBASE* or JSOP_INDEXBASE* ops, so it is
* useful only when checking for optimization opportunities.