104442 - JS Engine doesn't appear to know where a variable was declared.

This commit is contained in:
Fedor 2019-09-05 20:06:29 +03:00
parent 4cb03fb6ec
commit bc64f5e333
9 changed files with 457 additions and 35 deletions

View File

@ -219,4 +219,22 @@ describe("PageError component:", () => {
expect(locationLink3.length).toBe(1);
expect(locationLink3.text()).toBe("test3.js:9:4");
});
it("displays error notes", () => {
const message = stubPreparedMessages.get("SyntaxError: redeclaration of let a");
let wrapper = render(PageError({ message, serviceContainer }));
const notes = wrapper.find(".error-note");
expect(notes.length).toBe(1);
const note = notes.eq(0);
expect(note.find(".message-body").text())
.toBe("note: Previously declared at line 2, column 6");
// There should be the location.
const locationLink = note.find(`.message-location`);
expect(locationLink.length).toBe(1);
expect(locationLink.text()).toBe("test-console-api.html:2:6");
});
});

View File

@ -140,6 +140,10 @@ pageError.set("Reference Error", `
foo()
`);
pageError.set("Redeclaration Error", `
let a, a;
`);
module.exports = {
consoleApi,
evaluationResult,

View File

@ -52,6 +52,49 @@ stubPreparedMessages.set("ReferenceError: asdf is not defined", new ConsoleMessa
"notes": null
}));
stubPreparedMessages.set("SyntaxError: redeclaration of let a", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "log",
"level": "error",
"messageText": "SyntaxError: redeclaration of let a",
"parameters": null,
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"log\",\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":[{\"messageBody\":\"Previously declared at line 2, column 6\",\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":6}}]}",
"stacktrace": [
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
"lineNumber": 6,
"columnNumber": 9,
"functionName": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null
}
],
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 2,
"column": 9
},
"groupId": null,
"userProvidedStyles": null,
"notes": [
{
"messageBody": "Previously declared at line 2, column 6",
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 2,
"column": 6
}
}
]
}));
stubPackets.set("ReferenceError: asdf is not defined", {
"from": "server1.conn0.child1/consoleActor2",
"type": "pageError",
@ -95,6 +138,50 @@ stubPackets.set("ReferenceError: asdf is not defined", {
}
});
stubPackets.set("SyntaxError: redeclaration of let a", {
"from": "server1.conn0.child1/consoleActor2",
"type": "pageError",
"pageError": {
"errorMessage": "SyntaxError: redeclaration of let a",
"errorMessageName": "JSMSG_REDECLARED_VAR",
"sourceName": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"lineText": " let a, a;\n",
"lineNumber": 2,
"columnNumber": 9,
"category": "content javascript",
"warning": false,
"error": false,
"exception": true,
"strict": false,
"info": false,
"private": false,
"stacktrace": [
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
"lineNumber": 6,
"columnNumber": 9,
"functionName": null
},
{
"filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
"lineNumber": 53,
"columnNumber": 20,
"functionName": null
}
],
"notes": [
{
"messageBody": "Previously declared at line 2, column 6",
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
"line": 2,
"column": 6
}
}
]
}
});
module.exports = {
stubPreparedMessages,
stubPackets,

View File

@ -112,6 +112,24 @@ function doPageErrors()
warning: true,
exception: false,
},
"let a, a;": {
errorMessage: /redeclaration of/,
errorMessageName: "JSMSG_REDECLARED_VAR",
sourceName: /test_page_errors/,
category: "chrome javascript",
timeStamp: /^\d+$/,
error: false,
warning: false,
exception: true,
notes: [
{
messageBody: /Previously declared at line/,
frame: {
source: /test_page_errors/,
}
}
]
},
};
let container = document.createElement("script");

View File

@ -124,6 +124,7 @@ DeclarationKindIsLexical(DeclarationKind kind)
// Used in Parser to track declared names.
class DeclaredNameInfo
{
uint32_t pos_;
DeclarationKind kind_;
// If the declared name is a binding, whether the binding is closed
@ -132,8 +133,9 @@ class DeclaredNameInfo
bool closedOver_;
public:
explicit DeclaredNameInfo(DeclarationKind kind)
: kind_(kind),
explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos)
: pos_(pos),
kind_(kind),
closedOver_(false)
{ }
@ -144,6 +146,12 @@ class DeclaredNameInfo
return kind_;
}
static const uint32_t npos = uint32_t(-1);
uint32_t pos() const {
return pos_;
}
void alterKind(DeclarationKind kind) {
kind_ = kind;
}

View File

@ -19,6 +19,8 @@
#include "frontend/Parser.h"
#include "mozilla/Sprintf.h"
#include <new>
#include "jsapi.h"
@ -200,11 +202,12 @@ ParseContext::Scope::addCatchParameters(ParseContext* pc, Scope& catchParamScope
for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) {
DeclarationKind kind = r.front().value()->kind();
uint32_t pos = r.front().value()->pos();
MOZ_ASSERT(DeclarationKindIsCatchParameter(kind));
JSAtom* name = r.front().key();
AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
if (!addDeclaredName(pc, p, name, kind))
if (!addDeclaredName(pc, p, name, kind, pos))
return false;
}
@ -343,7 +346,8 @@ ParseContext::init()
namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName());
MOZ_ASSERT(!p);
if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(),
DeclarationKind::Const))
DeclarationKind::Const,
DeclaredNameInfo::npos))
{
return false;
}
@ -1001,13 +1005,42 @@ Parser<ParseHandler>::hasValidSimpleStrictParameterNames()
template <typename ParseHandler>
void
Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind kind,
TokenPos pos)
Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
TokenPos pos, uint32_t prevPos)
{
JSAutoByteString bytes;
if (!AtomToPrintableString(context, name, &bytes))
return;
errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(kind), bytes.ptr());
if (prevPos == DeclaredNameInfo::npos) {
errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
return;
}
auto notes = MakeUnique<JSErrorNotes>();
if (!notes)
return;
uint32_t line, column;
tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
const size_t MaxWidth = sizeof("4294967295");
char columnNumber[MaxWidth];
SprintfLiteral(columnNumber, "%" PRIu32, column);
char lineNumber[MaxWidth];
SprintfLiteral(lineNumber, "%" PRIu32, line);
if (!notes->addNoteLatin1(pc->sc()->context,
getFilename(), line, column,
GetErrorMessage, nullptr,
JSMSG_REDECLARED_PREV,
lineNumber, columnNumber))
{
return;
}
errorWithNotesAt(Move(notes), pos.begin, JSMSG_REDECLARED_VAR,
DeclarationKindString(prevKind), bytes.ptr());
}
// notePositionalFormalParameter is called for both the arguments of a regular
@ -1022,6 +1055,7 @@ Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKi
template <typename ParseHandler>
bool
Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
uint32_t beginPos,
bool disallowDuplicateParams,
bool* duplicatedParam)
{
@ -1046,7 +1080,7 @@ Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName
*duplicatedParam = true;
} else {
DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos))
return false;
}
@ -1150,8 +1184,8 @@ DeclarationKindIsParameter(DeclarationKind kind)
template <typename ParseHandler>
bool
Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
Maybe<DeclarationKind>* redeclaredKind)
Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos)
{
MOZ_ASSERT(DeclarationKindIsVar(kind));
@ -1224,23 +1258,29 @@ Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kin
if (!annexB35Allowance && !annexB33Allowance) {
*redeclaredKind = Some(declaredKind);
*prevPos = p->value()->pos();
return true;
}
} else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) {
MOZ_ASSERT(DeclarationKindIsParameter(declaredKind));
// Annex B.3.3.1 disallows redeclaring parameter names.
// We don't need to set *prevPos here since this case is not
// an error.
*redeclaredKind = Some(declaredKind);
return true;
}
} else {
if (!scope->addDeclaredName(pc, p, name, kind))
if (!scope->addDeclaredName(pc, p, name, kind, beginPos))
return false;
}
}
if (!pc->sc()->strict() && pc->sc()->isEvalContext())
if (!pc->sc()->strict() && pc->sc()->isEvalContext()) {
*redeclaredKind = isVarRedeclaredInEval(name, kind);
// We don't have position information at runtime.
*prevPos = DeclaredNameInfo::npos;
}
return true;
}
@ -1248,11 +1288,15 @@ Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kin
template <typename ParseHandler>
bool
Parser<ParseHandler>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
bool* tryAnnexB)
uint32_t beginPos, bool* tryAnnexB)
{
Maybe<DeclarationKind> redeclaredKind;
if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind))
uint32_t unused;
if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos,
&redeclaredKind, &unused))
{
return false;
}
if (!redeclaredKind && pc->isFunctionBox()) {
ParseContext::Scope& funScope = pc->functionScope();
@ -1324,11 +1368,12 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
case DeclarationKind::BodyLevelFunction:
case DeclarationKind::ForOfVar: {
Maybe<DeclarationKind> redeclaredKind;
if (!tryDeclareVar(name, kind, &redeclaredKind))
uint32_t prevPos;
if (!tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos))
return false;
if (redeclaredKind) {
reportRedeclaration(name, *redeclaredKind, pos);
reportRedeclaration(name, *redeclaredKind, pos, prevPos);
return false;
}
@ -1345,7 +1390,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
return false;
}
if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
if (!pc->functionScope().addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
@ -1368,7 +1413,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
(p->value()->kind() != DeclarationKind::LexicalFunction &&
p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction))
{
reportRedeclaration(name, p->value()->kind(), pos);
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
@ -1376,7 +1421,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
// declaration that shadows the VarForAnnexBLexicalFunction.
p->value()->alterKind(kind);
} else {
if (!scope->addDeclaredName(pc, p, name, kind))
if (!scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
}
@ -1416,7 +1461,7 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
if (pc->isFunctionExtraBodyVarScopeInnermost()) {
DeclaredNamePtr p = pc->functionScope().lookupDeclaredName(name);
if (p && DeclarationKindIsParameter(p->value()->kind())) {
reportRedeclaration(name, p->value()->kind(), pos);
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
@ -1433,12 +1478,12 @@ Parser<ParseHandler>::noteDeclaredName(HandlePropertyName name, DeclarationKind
p = scope->lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
} else {
reportRedeclaration(name, p->value()->kind(), pos);
reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
if (!p && !scope->addDeclaredName(pc, p, name, kind))
if (!p && !scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
@ -2188,8 +2233,11 @@ Parser<ParseHandler>::declareFunctionThis()
ParseContext::Scope& funScope = pc->functionScope();
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
MOZ_ASSERT(!p);
if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var))
if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var,
DeclaredNameInfo::npos))
{
return false;
}
funbox->setHasThisBinding();
}
@ -2231,8 +2279,11 @@ Parser<ParseHandler>::declareDotGeneratorName()
ParseContext::Scope& funScope = pc->functionScope();
HandlePropertyName dotGenerator = context->names().dotGenerator;
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var))
if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var,
DeclaredNameInfo::npos))
{
return false;
}
return true;
}
@ -2481,8 +2532,11 @@ Parser<ParseHandler>::declareFunctionArgumentsObject()
if (tryDeclareArguments) {
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
if (!p) {
if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var))
if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var,
DeclaredNameInfo::npos))
{
return false;
}
funbox->declaredArguments = true;
funbox->usesArguments = true;
} else if (hasExtraBodyVarScope) {
@ -2964,8 +3018,8 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
if (!name)
return false;
if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
&duplicatedParam))
if (!notePositionalFormalParameter(funcpn, name, pos().begin,
disallowDuplicateParams, &duplicatedParam))
{
return false;
}
@ -3689,7 +3743,7 @@ Parser<ParseHandler>::functionStmt(uint32_t preludeStart, YieldHandling yieldHan
// early error, do so. This 'var' binding would be assigned
// the function object when its declaration is reached, not at
// the start of the block.
if (!tryDeclareVarForAnnexBLexicalFunction(name, &tryAnnexB))
if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB))
return null();
}

View File

@ -146,9 +146,9 @@ class ParseContext : public Nestable<ParseContext>
}
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name,
DeclarationKind kind)
DeclarationKind kind, uint32_t pos)
{
return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind)));
return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
}
// Remove all VarForAnnexBLexicalFunction declarations of a certain
@ -1437,15 +1437,17 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
bool hasValidSimpleStrictParameterNames();
void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
bool notePositionalFormalParameter(Node fn, HandlePropertyName name,
void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
uint32_t prevPos);
bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
bool disallowDuplicateParams, bool* duplicatedParam);
bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
mozilla::Maybe<DeclarationKind>* redeclaredKind);
bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, bool* tryAnnexB);
bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, uint32_t beginPos,
bool* tryAnnexB);
bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
DeclarationKind kind, TokenPos pos);
bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);

View File

@ -0,0 +1,230 @@
// Error message for redeclaration should show the position where the variable
// was declared.
const npos = -1;
function test_one(fun, filename, name,
[prevLineNumber, prevColumnNumber],
[lineNumber, columnNumber]) {
let caught = false;
try {
fun();
} catch (e) {
assertEq(e.message.includes("redeclaration"), true);
assertEq(e.lineNumber, lineNumber);
assertEq(e.columnNumber, columnNumber);
let notes = getErrorNotes(e);
if (prevLineNumber == npos) {
assertEq(notes.length, 0);
} else {
assertEq(notes.length, 1);
let note = notes[0];
assertEq(note.message,
`Previously declared at line ${prevLineNumber}, column ${prevColumnNumber}`);
assertEq(note.lineNumber, prevLineNumber);
assertEq(note.columnNumber, prevColumnNumber);
if (filename)
assertEq(note.fileName, filename);
}
caught = true;
}
assertEq(caught, true);
}
function test_parse(source, ...args) {
test_one(() => {
Reflect.parse(source, { source: "foo.js" });
}, "foo.js", ...args);
}
function test_eval(source, ...args) {
test_one(() => {
eval(source);
}, undefined, ...args);
}
function test(...args) {
test_parse(...args);
test_eval(...args);
}
// let
test(`
let a, a;
`, "a", [2, 4], [2, 7]);
test(`
let a;
let a;
`, "a", [2, 4], [3, 4]);
test(`
let a;
const a = 1;
`, "a", [2, 4], [3, 6]);
test(`
let a;
var a;
`, "a", [2, 4], [3, 4]);
test(`
let a;
function a() {
}
`, "a", [2, 4], [3, 9]);
test(`
{
let a;
function a() {
}
}
`, "a", [3, 6], [4, 11]);
// const
test(`
const a = 1, a = 2;
`, "a", [2, 6], [2, 13]);
test(`
const a = 1;
const a = 2;
`, "a", [2, 6], [3, 6]);
test(`
const a = 1;
let a;
`, "a", [2, 6], [3, 4]);
test(`
const a = 1;
var a;
`, "a", [2, 6], [3, 4]);
test(`
const a = 1;
function a() {
}
`, "a", [2, 6], [3, 9]);
test(`
{
const a = 1;
function a() {
}
}
`, "a", [3, 8], [4, 11]);
// var
test(`
var a;
let a;
`, "a", [2, 4], [3, 4]);
test(`
var a;
const a = 1;
`, "a", [2, 4], [3, 6]);
// function
test(`
function a() {};
let a;
`, "a", [2, 9], [3, 4]);
test(`
function a() {};
const a = 1;
`, "a", [2, 9], [3, 6]);
// Annex B lexical function
test(`
{
function a() {};
let a;
}
`, "a", [3, 11], [4, 6]);
test(`
{
function a() {};
const a = 1;
}
`, "a", [3, 11], [4, 8]);
// catch parameter
test(`
try {
} catch (a) {
let a;
}
`, "a", [3, 9], [4, 6]);
test(`
try {
} catch (a) {
const a = 1;
}
`, "a", [3, 9], [4, 8]);
test(`
try {
} catch (a) {
function a() {
}
}
`, "a", [3, 9], [4, 11]);
// parameter
test(`
function f(a) {
let a;
}
`, "a", [2, 11], [3, 6]);
test(`
function f(a) {
const a = 1;
}
`, "a", [2, 11], [3, 8]);
test(`
function f([a]) {
let a;
}
`, "a", [2, 12], [3, 6]);
test(`
function f({a}) {
let a;
}
`, "a", [2, 12], [3, 6]);
test(`
function f(...a) {
let a;
}
`, "a", [2, 14], [3, 6]);
test(`
function f(a=1) {
let a;
}
`, "a", [2, 11], [3, 6]);
// eval
// We don't have position information at runtime.
// No note should be shown.
test_eval(`
let a;
eval("var a");
`, "a", [npos, npos], [1, 4]);

View File

@ -63,6 +63,7 @@ MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due t
MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage")
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length")
MSG_DEF(JSMSG_REDECLARED_PREV, 2, JSEXN_NOTE, "Previously declared at line {0}, column {1}")
MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
MSG_DEF(JSMSG_GETTER_ONLY, 1, JSEXN_TYPEERR, "setting getter-only property {0}")