Bug 568142 - Part 1: Add column numbers to error reports. r=jorendorff

This commit is contained in:
Alex Crichton 2012-08-08 11:39:40 -07:00
parent 3a886a65cd
commit ffaca98dfe
13 changed files with 263 additions and 56 deletions

View File

@ -386,41 +386,58 @@ EmitBackPatchOp(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t *lastp)
return EmitJump(cx, bce, op, delta);
}
/* A macro for inlining at the top of EmitTree (whence it came). */
#define UPDATE_LINE_NUMBER_NOTES(cx, bce, line) \
JS_BEGIN_MACRO \
unsigned line_ = (line); \
unsigned delta_ = line_ - bce->currentLine(); \
if (delta_ != 0) { \
/* \
* Encode any change in the current source line number by using \
* either several SRC_NEWLINE notes or just one SRC_SETLINE note, \
* whichever consumes less space. \
* \
* NB: We handle backward line number deltas (possible with for \
* loops where the update part is emitted after the body, but its \
* line number is <= any line number in the body) here by letting \
* unsigned delta_ wrap to a very large number, which triggers a \
* SRC_SETLINE. \
*/ \
bce->current->currentLine = line_; \
if (delta_ >= (unsigned)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \
if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)line_) < 0) \
return false; \
} else { \
do { \
if (NewSrcNote(cx, bce, SRC_NEWLINE) < 0) \
return false; \
} while (--delta_ != 0); \
} \
} \
JS_END_MACRO
/* Updates line number notes, not column notes. */
static inline bool
UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, unsigned line)
{
unsigned delta = line - bce->currentLine();
if (delta != 0) {
/*
* Encode any change in the current source line number by using
* either several SRC_NEWLINE notes or just one SRC_SETLINE note,
* whichever consumes less space.
*
* NB: We handle backward line number deltas (possible with for
* loops where the update part is emitted after the body, but its
* line number is <= any line number in the body) here by letting
* unsigned delta_ wrap to a very large number, which triggers a
* SRC_SETLINE.
*/
bce->current->currentLine = line;
bce->current->lastColumn = 0;
if (delta >= (unsigned)(2 + ((line > SN_3BYTE_OFFSET_MASK)<<1))) {
if (NewSrcNote2(cx, bce, SRC_SETLINE, (ptrdiff_t)line) < 0)
return false;
} else {
do {
if (NewSrcNote(cx, bce, SRC_NEWLINE) < 0)
return false;
} while (--delta != 0);
}
}
return true;
}
/* A function, so that we avoid macro-bloating all the other callsites. */
static bool
UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, unsigned line)
UpdateSourceCoordNotes(JSContext *cx, BytecodeEmitter *bce, TokenPtr pos)
{
UPDATE_LINE_NUMBER_NOTES(cx, bce, line);
if (!UpdateLineNumberNotes(cx, bce, pos.lineno))
return false;
ptrdiff_t colspan = ptrdiff_t(pos.index) -
ptrdiff_t(bce->current->lastColumn);
if (colspan != 0) {
if (colspan < 0) {
colspan += SN_COLSPAN_DOMAIN;
} else if (colspan >= SN_COLSPAN_DOMAIN / 2) {
ReportStatementTooLarge(cx, bce->topStmt);
return false;
}
if (NewSrcNote2(cx, bce, SRC_COLSPAN, colspan) < 0)
return false;
bce->current->lastColumn = pos.index;
}
return true;
}
@ -436,7 +453,7 @@ EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
nextpn = nextpn->pn_head;
if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
if (!UpdateSourceCoordNotes(cx, bce, nextpn->pn_pos.begin))
return -1;
}
@ -451,7 +468,7 @@ EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
nextpn = nextpn->pn_head;
if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
if (!UpdateSourceCoordNotes(cx, bce, nextpn->pn_pos.begin))
return false;
}
@ -2690,7 +2707,7 @@ MaybeEmitVarDecl(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *
(!bce->sc->inFunction() || bce->sc->fun()->isHeavyweight()))
{
bce->switchToProlog();
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
return false;
if (!EmitIndexOp(cx, prologOp, atomIndex, bce))
return false;
@ -4105,7 +4122,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
/* Indicate that we're emitting a subroutine body. */
stmtInfo.type = STMT_SUBROUTINE;
if (!UpdateLineNumberNotes(cx, bce, pn->pn_kid3->pn_pos.begin.lineno))
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_kid3->pn_pos.begin))
return false;
if (Emit1(cx, bce, JSOP_FINALLY) < 0 ||
!EmitTree(cx, bce, pn->pn_kid3) ||
@ -4699,6 +4716,8 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
}
#endif
if (op == JSOP_POP) {
if (!UpdateSourceCoordNotes(cx, bce, pn3->pn_pos.begin))
return false;
if (!EmitTree(cx, bce, pn3))
return false;
if (pn3->isKind(PNK_VAR) || pn3->isKind(PNK_CONST) || pn3->isKind(PNK_LET)) {
@ -4762,6 +4781,8 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
/* Check for update code to do before the condition (if any). */
pn3 = forHead->pn_kid3;
if (pn3) {
if (!UpdateSourceCoordNotes(cx, bce, pn3->pn_pos.begin))
return false;
op = JSOP_POP;
#if JS_HAS_DESTRUCTURING
if (pn3->isKind(PNK_ASSIGN)) {
@ -4910,7 +4931,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
bce->switchToProlog();
if (!EmitIndex32(cx, JSOP_DEFFUN, index, bce))
return false;
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
return false;
bce->switchToMain();
@ -5102,6 +5123,9 @@ EmitContinue(JSContext *cx, BytecodeEmitter *bce, PropertyName *label)
static bool
EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
return false;
/* Push a return value */
if (ParseNode *pn2 = pn->pn_kid) {
if (!EmitTree(cx, bce, pn2))
@ -5179,6 +5203,9 @@ EmitStatement(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!pn2)
return true;
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
return false;
/*
* Top-level or called-from-a-native JS_Execute/EvaluateScript,
* debugger, and eval frames may need the value of the ultimate
@ -5927,6 +5954,8 @@ EmitArray(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
static bool
EmitUnary(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
{
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
return false;
/* Unary op, including unary +/-. */
JSOp op = pn->getOp();
ParseNode *pn2 = pn->pn_kid;
@ -6028,7 +6057,8 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
pn->pn_offset = top;
/* Emit notes to tell the current bytecode's source line number. */
UPDATE_LINE_NUMBER_NOTES(cx, bce, pn->pn_pos.begin.lineno);
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.begin.lineno))
return false;
switch (pn->getKind()) {
case PNK_FUNCTION:
@ -6627,7 +6657,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
/* bce->emitLevel == 1 means we're last on the stack, so finish up. */
if (ok && bce->emitLevel == 1) {
if (!UpdateLineNumberNotes(cx, bce, pn->pn_pos.end.lineno))
if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.end))
return false;
}
@ -7092,7 +7122,7 @@ JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
{"switch", 2},
{"funcdef", 1},
{"catch", 1},
{"unused", -1},
{"colspan", 1},
{"newline", 0},
{"setline", 1},
{"xdelta", 0},

View File

@ -73,6 +73,8 @@ struct BytecodeEmitter
unsigned noteLimit; /* limit number for source notes in notePool */
ptrdiff_t lastNoteOffset; /* code offset for last source note */
unsigned currentLine; /* line number for tree-based srcnote gen */
unsigned lastColumn; /* zero-based column index on currentLine of
last SRC_COLSPAN-annotated opcode */
} prolog, main, *current;
Parser *const parser; /* the parser */
@ -174,6 +176,7 @@ struct BytecodeEmitter
unsigned noteLimit() const { return current->noteLimit; }
ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; }
unsigned currentLine() const { return current->currentLine; }
unsigned lastColumn() const { return current->lastColumn; }
inline ptrdiff_t countFinalSourceNotes();
@ -249,7 +252,7 @@ EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *body);
* +---------+-----+ +---+-----------+
*
* At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE,
* SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.
* SRC_COLSPAN, SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.
*
* NB: the js_SrcNoteSpec array in BytecodeEmitter.cpp is indexed by this
* enum, so its initializers need to match the order here.
@ -310,7 +313,7 @@ enum SrcNoteType {
SRC_SWITCHBREAK = 18, /* JSOP_GOTO is a break in a switch */
SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */
SRC_CATCH = 20, /* catch block has guard */
/* 21 is unused */
SRC_COLSPAN = 21, /* number of columns this opcode spans */
SRC_NEWLINE = 22, /* bytecode follows a source newline */
SRC_SETLINE = 23, /* a file-absolute source line number note */
SRC_XDELTA = 24 /* 24-31 are for extended delta notes */
@ -346,7 +349,7 @@ enum SrcNoteType {
? SRC_XDELTA \
: *(sn) >> SN_DELTA_BITS))
#define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn))
#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE)
#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_COLSPAN)
#define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \
? *(sn) & SN_XDELTA_MASK \
@ -366,6 +369,19 @@ enum SrcNoteType {
#define SN_3BYTE_OFFSET_FLAG 0x80
#define SN_3BYTE_OFFSET_MASK 0x7f
/*
* Negative SRC_COLSPAN offsets are rare, but can arise with for(;;) loops and
* other constructs that generate code in non-source order. They can also arise
* due to failure to update pn->pn_pos.end to be the last child's end -- such
* failures are bugs to fix.
*
* Source note offsets in general must be non-negative and less than 0x800000,
* per the above SN_3BYTE_* definitions. To encode negative colspans, we bias
* them by the offset domain size and restrict non-negative colspans to less
* than half this domain.
*/
#define SN_COLSPAN_DOMAIN ptrdiff_t(SN_3BYTE_OFFSET_FLAG << 16)
#define SN_MAX_OFFSET ((size_t)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16) - 1)
#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \

View File

@ -259,7 +259,7 @@ def check_output(out, err, rc, test):
if rc != test.expect_status:
# Allow a non-zero exit code if we want to allow OOM, but only if we
# actually got OOM.
return test.allow_oom and ': out of memory' in err and 'Assertion failure' not in err
return test.allow_oom and 'out of memory' in err and 'Assertion failure' not in err
return True

View File

@ -65,6 +65,7 @@ CPPSRCS = \
testXDR.cpp \
testProfileStrings.cpp \
testJSEvaluateScript.cpp \
testErrorCopying.cpp \
$(NULL)
CSRCS = \

View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=99:
*
* Tests that the column number of error reports is properly copied over from
* other reports when invoked from the C++ api.
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "tests.h"
#include "jscntxt.h"
static uint32_t column = 0;
static void
my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
column = report->column;
}
BEGIN_TEST(testErrorCopying_columnCopied)
{
//0 1 2
//0123456789012345678901234567
EXEC("function check() { Object; foo; }");
JS::RootedValue rval(cx);
JS_SetErrorReporter(cx, my_ErrorReporter);
CHECK(!JS_CallFunctionName(cx, global, "check", 0, NULL, rval.address()));
CHECK(column == 27);
return true;
}
END_TEST(testErrorCopying_columnCopied)

View File

@ -6039,6 +6039,7 @@ struct JSErrorReport {
const jschar *ucmessage; /* the (default) error message */
const jschar **messageArgs; /* arguments for the error message */
int16_t exnType; /* One of the JSExnType constants */
unsigned column; /* zero-based column index in line */
};
/*

View File

@ -43,6 +43,7 @@ DEFINE_ATOM(call, "call")
DEFINE_ATOM(callee, "callee")
DEFINE_ATOM(caller, "caller")
DEFINE_ATOM(classPrototype, "prototype")
DEFINE_ATOM(columnNumber, "columnNumber")
DEFINE_ATOM(constructor, "constructor")
DEFINE_ATOM(each, "each")
DEFINE_ATOM(eval, "eval")

View File

@ -474,7 +474,7 @@ PopulateReportBlame(JSContext *cx, JSErrorReport *report)
return;
report->filename = iter.script()->filename;
report->lineno = PCToLineNumber(iter.script(), iter.pc());
report->lineno = PCToLineNumber(iter.script(), iter.pc(), &report->column);
report->originPrincipals = iter.script()->originPrincipals;
}

View File

@ -96,6 +96,7 @@ struct JSExnPrivate
js::HeapPtrString message;
js::HeapPtrString filename;
unsigned lineno;
unsigned column;
size_t stackDepth;
int exnType;
JSStackTraceElem stackElems[1];
@ -216,6 +217,7 @@ CopyErrorReport(JSContext *cx, JSErrorReport *report)
/* Copy non-pointer members. */
copy->lineno = report->lineno;
copy->column = report->column;
copy->errorNumber = report->errorNumber;
copy->exnType = report->exnType;
@ -250,7 +252,8 @@ SetExnPrivate(JSContext *cx, JSObject *exnObject, JSExnPrivate *priv);
static bool
InitExnPrivate(JSContext *cx, HandleObject exnObject, HandleString message,
HandleString filename, unsigned lineno, JSErrorReport *report, int exnType)
HandleString filename, unsigned lineno, unsigned column,
JSErrorReport *report, int exnType)
{
JS_ASSERT(exnObject->isError());
JS_ASSERT(!exnObject->getPrivate());
@ -326,6 +329,7 @@ InitExnPrivate(JSContext *cx, HandleObject exnObject, HandleString message,
priv->message.init(message);
priv->filename.init(filename);
priv->lineno = lineno;
priv->column = column;
priv->stackDepth = frames.length();
priv->exnType = exnType;
for (size_t i = 0; i < frames.length(); ++i) {
@ -436,7 +440,15 @@ exn_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
atom = cx->runtime->atomState.lineNumberAtom;
if (str == atom) {
prop = js_lineNumber_str;
v = INT_TO_JSVAL(priv->lineno);
v = UINT_TO_JSVAL(priv->lineno);
attrs = JSPROP_ENUMERATE;
goto define;
}
atom = cx->runtime->atomState.columnNumberAtom;
if (str == atom) {
prop = js_columnNumber_str;
v = UINT_TO_JSVAL(priv->column);
attrs = JSPROP_ENUMERATE;
goto define;
}
@ -585,16 +597,16 @@ Exception(JSContext *cx, unsigned argc, Value *vp)
}
/* Set the 'lineNumber' property. */
uint32_t lineno;
uint32_t lineno, column = 0;
if (args.length() > 2) {
if (!ToUint32(cx, args[2], &lineno))
return false;
} else {
lineno = iter.done() ? 0 : PCToLineNumber(iter.script(), iter.pc());
lineno = iter.done() ? 0 : PCToLineNumber(iter.script(), iter.pc(), &column);
}
int exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32();
if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType))
if (!InitExnPrivate(cx, obj, message, filename, lineno, column, NULL, exnType))
return false;
args.rval().setObject(*obj);
@ -791,6 +803,7 @@ InitErrorClass(JSContext *cx, Handle<GlobalObject*> global, int type, HandleObje
RootedId messageId(cx, NameToId(cx->runtime->atomState.messageAtom));
RootedId fileNameId(cx, NameToId(cx->runtime->atomState.fileNameAtom));
RootedId lineNumberId(cx, NameToId(cx->runtime->atomState.lineNumberAtom));
RootedId columnNumberId(cx, NameToId(cx->runtime->atomState.columnNumberAtom));
if (!DefineNativeProperty(cx, errorProto, nameId, nameValue,
JS_PropertyStub, JS_StrictPropertyStub, 0, 0, 0) ||
!DefineNativeProperty(cx, errorProto, messageId, empty,
@ -798,6 +811,8 @@ InitErrorClass(JSContext *cx, Handle<GlobalObject*> global, int type, HandleObje
!DefineNativeProperty(cx, errorProto, fileNameId, empty,
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
!DefineNativeProperty(cx, errorProto, lineNumberId, zeroValue,
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
!DefineNativeProperty(cx, errorProto, columnNumberId, zeroValue,
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0))
{
return NULL;
@ -988,7 +1003,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
tv[3] = STRING_TO_JSVAL(filenameStr);
if (!InitExnPrivate(cx, errObject, messageStr, filenameStr,
reportp->lineno, reportp, exn)) {
reportp->lineno, reportp->column, reportp, exn)) {
return false;
}
@ -1108,11 +1123,19 @@ js_ReportUncaughtException(JSContext *cx)
lineno = 0;
}
uint32_t column;
if (!JS_GetProperty(cx, exnObject, js_columnNumber_str, &roots[5]) ||
!ToUint32(cx, roots[5], &column))
{
column = 0;
}
reportp = &report;
PodZero(&report);
report.filename = filename.ptr();
report.lineno = (unsigned) lineno;
report.exnType = int16_t(JSEXN_NONE);
report.column = (unsigned) column;
if (str) {
if (JSFixedString *fixed = str->ensureFixed(cx))
report.ucmessage = fixed->chars();
@ -1183,6 +1206,7 @@ js_CopyErrorObject(JSContext *cx, HandleObject errobj, HandleObject scope)
return NULL;
JS::Anchor<JSString *> filenameAnchor(copy->filename);
copy->lineno = priv->lineno;
copy->column = priv->column;
copy->stackDepth = 0;
copy->exnType = priv->exnType;

View File

@ -1881,9 +1881,11 @@ js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
}
unsigned
js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc)
js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc,
unsigned *columnp)
{
unsigned lineno = startLine;
unsigned column = 0;
/*
* Walk through source notes accumulating their deltas, keeping track of
@ -1898,25 +1900,41 @@ js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbyt
if (type == SRC_SETLINE) {
if (offset <= target)
lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
column = 0;
} else if (type == SRC_NEWLINE) {
if (offset <= target)
lineno++;
column = 0;
}
if (offset > target)
break;
if (type == SRC_COLSPAN) {
ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0);
if (colspan >= SN_COLSPAN_DOMAIN / 2)
colspan -= SN_COLSPAN_DOMAIN;
JS_ASSERT(ptrdiff_t(column) + colspan >= 0);
column += colspan;
}
}
if (columnp)
*columnp = column;
return lineno;
}
unsigned
js::PCToLineNumber(JSScript *script, jsbytecode *pc)
js::PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp)
{
/* Cope with StackFrame.pc value prior to entering js_Interpret. */
if (!pc)
return 0;
return PCToLineNumber(script->lineno, script->notes(), script->code, pc);
return PCToLineNumber(script->lineno, script->notes(), script->code, pc,
columnp);
}
/* The line number limit is the same as the jssrcnote offset limit. */

View File

@ -1180,10 +1180,11 @@ js_GetScriptLineExtent(JSScript *script);
namespace js {
extern unsigned
PCToLineNumber(JSScript *script, jsbytecode *pc);
PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp = NULL);
extern unsigned
PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc);
PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc,
unsigned *columnp = NULL);
extern unsigned
CurrentLine(JSContext *cx);

View File

@ -1583,6 +1583,7 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
"ofs", "line", "pc", "delta", "desc", "args");
Sprint(sp, "---- ---- ----- ------ -------- ------\n");
unsigned offset = 0;
unsigned colspan = 0;
unsigned lineno = script->lineno;
jssrcnote *notes = script->notes();
unsigned switchTableEnd = 0, switchTableStart = 0;
@ -1598,6 +1599,12 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp)
}
Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name);
switch (type) {
case SRC_COLSPAN:
colspan = js_GetSrcNoteOffset(sn, 0);
if (colspan >= SN_COLSPAN_DOMAIN / 2)
colspan -= SN_COLSPAN_DOMAIN;
Sprint(sp, "%d", colspan);
break;
case SRC_SETLINE:
lineno = js_GetSrcNoteOffset(sn, 0);
Sprint(sp, " lineno %u", lineno);
@ -4290,7 +4297,7 @@ my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
prefix = JS_smprintf("%s:", report->filename);
if (report->lineno) {
tmp = prefix;
prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
prefix = JS_smprintf("%s%u:%u ", tmp ? tmp : "", report->lineno, report->column);
JS_free(cx, tmp);
}
if (JSREPORT_IS_WARNING(report->flags)) {

View File

@ -0,0 +1,73 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 568142;
var summary = 'error reporting blames column as well as line';
function test(f, col) {
var caught = false;
try {
f();
} catch (e) {
caught = true;
assertEq(e.columnNumber, col);
}
assertEq(caught, true);
}
/* Note single hard tab before return! */
function foo(o) {
return o.p;
}
test(foo, 1);
//234567890123456789
test(function(f) { return f.bar; }, 19);
test(function(f) { return f(); }, 19);
/* Cover negative colspan case using for(;;) loop with error in update part. */
test(function(){
//0 1 2 3 4
//012345678901234567890123456789012345678901
eval("function baz() { for (var i = 0; i < 10; i += a.b); assertEq(i !== i, true); }");
baz();
}, 41);
// 1 2 3
//234567890123456789012345678901234
test(function() { var tmp = null; tmp(); }, 34)
test(function() { var tmp = null; tmp.foo; }, 35)
/* Just a generic 'throw'. */
test(function() {
//234567890123
foo({}); throw new Error('a');
}, 13);
/* Be sure to report the right statement */
test(function() {
function f() { return true; }
function g() { return false; }
//234567890123456789012345678
f(); g(); f(); if (f()) a += e;
}, 28);
//2345678901234567890
test(function() { e++; }, 18);
test(function() {print += e; }, 17);
test(function(){e += 1 }, 16);
test(function() { print[e]; }, 19);
test(function() { e[1]; }, 18);
test(function() { e(); }, 18);
test(function() { 1(); }, 18);
test(function() { Object.defineProperty() }, 18);
test(function() {
//23456789012345678901
function foo() { asdf; } foo()
}, 21);
reportCompare(0, 0, "ok");
printStatus("All tests passed!");