Bug 1164741 - Readd parsing support for |for (var ...1 = ...2 in ...3)|, but completely ignore the |= ...2| assignment when ascribing semantics to it. r=jorendorff, r=efaust

This commit is contained in:
Jeff Walden 2015-05-13 21:13:02 -07:00
parent b12b980dd9
commit f9b496da64
7 changed files with 295 additions and 6 deletions

View File

@ -3944,12 +3944,26 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
if (!init)
return null();
// Ban the nonsensical |for (var V = E1 in E2);| where V is a
// destructuring pattern. See bug 1164741 for background.
if (pc->parsingForInit && kind == PNK_VAR) {
TokenKind afterInit;
if (!tokenStream.peekToken(&afterInit))
return null();
if (afterInit == TOK_IN) {
report(ParseError, false, init, JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,
"in");
return null();
}
}
if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
return null();
pn2 = handler.newBinary(PNK_ASSIGN, pn2, init);
if (!pn2)
return null();
handler.addList(pn, pn2);
break;
}
@ -3997,11 +4011,34 @@ Parser<ParseHandler>::variables(YieldHandling yieldHandling,
if (!init)
return null();
if (!bindBeforeInitializer && !data.binder(&data, name, this))
return null();
// Ignore an initializer if we have a for-in loop declaring a
// |var| with an initializer: |for (var v = ... in ...);|.
// Warn that this syntax is invalid so that developers looking
// in the console know to fix this. ES<6 permitted the
// initializer while ES6 doesn't; ignoring it seems the best
// way to incrementally move to ES6 semantics.
bool performAssignment = true;
if (pc->parsingForInit && kind == PNK_VAR) {
TokenKind afterInit;
if (!tokenStream.peekToken(&afterInit))
return null();
if (afterInit == TOK_IN) {
performAssignment = false;
if (!report(ParseWarning, pc->sc->strict(), init,
JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT, "in"))
{
return null();
}
}
}
if (!handler.finishInitializerAssignment(pn2, init, data.op))
return null();
if (performAssignment) {
if (!bindBeforeInitializer && !data.binder(&data, name, this))
return null();
if (!handler.finishInitializerAssignment(pn2, init, data.op))
return null();
}
} else {
if (data.isConst && !pc->parsingForInit) {
report(ParseError, false, null(), JSMSG_BAD_CONST_DECL);
@ -4885,8 +4922,14 @@ Parser<FullParseHandler>::forStatement(YieldHandling yieldHandling)
if (isForDecl) {
pn2 = pn1->pn_head;
if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) {
// We have a bizarre |for (var/const/let x = ... in/of ...)|
// loop erroneously permitted by ES1-5 but removed in ES6.
MOZ_ASSERT(!(headKind == PNK_FORIN && pn1->isKind(PNK_VAR)),
"Parser::variables should have ignored the "
"initializer in the ES5-sanctioned, ES6-prohibited "
"|for (var ... = ... in ...)| syntax");
// Otherwise, this bizarre |for (const/let x = ... in/of ...)|
// loop isn't valid ES6 and has never been permitted in
// SpiderMonkey.
report(ParseError, false, pn2, JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,
headKind == PNK_FOROF ? "of" : "in");
return null();

View File

@ -0,0 +1,130 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = "for-in-with-destructuring-assignments.js";
var BUGNUMBER = 1164741;
var summary = "|for (var <pat> = ... in ...)| is invalid syntax";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
// This is a total grab-bag of junk originally in tests changed when this
// syntax was removed. Avert your eyes!
assertThrowsInstanceOf(() => eval(`
for (var [x] = x>>x in [[]<[]])
{
[];
}`),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(function() {
// Abandon all hope, ye who try to read this.
eval(`
(function () {
for
(var [x] = function(){}
in
(function m(a) {
if (a < 1) {
x;
return;
}
return m(a - 1) + m(a - 2);
})(7)(eval(""))
)
{
[];
}
})
`)();
}, SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval(`
for (var [e] = [] in (eval("for (b = 0; b < 6; ++b) gc()"))) {}
`), SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval("for (var [ v , c ] = 0 in undefined) { }"),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval("var b = e; for (var [e] = b in w) c"),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval("for (var {a: []} = 2 in []) { }"),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval(`try
{
for (var [,{y}] = 1 in []) {}
}
catch(ex)
{
}`),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval("for (var [x] = [] in null);"),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval("for (var [x] = x in y) var x;"),
SyntaxError);
/******************************************************************************/
assertThrowsInstanceOf(() => eval(`
for (var [arguments] = ({ get y(){} }) in y ) (x);
`),
SyntaxError);
/******************************************************************************/
if (typeof evalcx == 'function') {
var src = 'try {\n' +
' for (var [e] = /x/ in d) {\n' +
' (function () {});\n' +
' }\n' +
'} catch (e) {}\n' +
'try {\n' +
' let(x = Object.freeze(this, /x/))\n' +
' e = {}.toString\n' +
' function y() {}\n' +
'} catch (e) {}';
try
{
evalcx(src);
throw new Error("didn't throw");
}
catch (e)
{
assertEq(e.name === "SyntaxError", true,
"expected invalid syntax, got " + e);
}
}
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -0,0 +1,32 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = "for-of-var-with-initializer.js";
var BUGNUMBER = 1164741;
var summary = "Don't assert parsing |for (var x = 3 of 42);|";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
try
{
Function("for (var x = 3 of 42);");
throw new Error("didn't throw");
}
catch (e)
{
assertEq(e instanceof SyntaxError, true,
"expected syntax error, got: " + e);
}
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

View File

@ -0,0 +1,74 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = "for-in-with-assignments.js";
var BUGNUMBER = 1164741;
var summary =
"Parse |for (var ... = ... in ...)| but execute it as if the assignment " +
"weren't there";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
// This is a total grab-bag of junk originally in tests changed when this
// syntax was removed. Leaving it all in one file will make it easier to
// eventually remove. Avert your eyes!
if (typeof Reflect !== "undefined")
Reflect.parse("for (var x = 3 in []) { }");
/******************************************************************************/
function testQ() {
try {
for (var i = 0 in this) throw p;
} catch (e) {
if (i !== 94)
return "what";
}
}
testQ();
/******************************************************************************/
function f3(i,o){for(var x=i in o)parseInt(o[x]); return x}
function f4(i,o){with(this)for(var x=i in o)parseInt(o[x]); return x}
assertEq(f3(42, []), undefined);
assertEq(f3(42, ['first']), "0");
assertEq(f4(42, []), undefined);
assertEq(f4(42, ['first']), "0");
/******************************************************************************/
function SetLangHead(l){
with(p){
for(var i=0 in x)
if(getElementById("TxtH"+i)!=undefined)
parseInt('huh');
}
}
x=[0,1,2,3];
p={getElementById: function (id){parseInt(uneval(this), id); return undefined;}};
SetLangHead(1);
/******************************************************************************/
(function(){for(var x = arguments in []){} function x(){}})();
/******************************************************************************/
with (0)
for (var b = 0 in 0); // don't assert in parser
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -36,6 +36,16 @@ assertError("for each (const x in foo);", SyntaxError);
assertError("for each (const {a:x,b:y,c:z} in foo);", SyntaxError);
assertError("for each (const [x,y,z] in foo);", SyntaxError);
assertError("for (x = 22 in foo);", SyntaxError);-
assertError("for ({a:x,b:y,c:z} = 22 in foo);", SyntaxError);
assertError("for ([x,y,z] = 22 in foo);", SyntaxError);
assertError("for (const x = 22 in foo);", SyntaxError);
assertError("for (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError);
assertError("for (const [x,y,z] = 22 in foo);", SyntaxError);
assertError("for each (const x = 22 in foo);", SyntaxError);
assertError("for each (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError);
assertError("for each (const [x,y,z] = 22 in foo);", SyntaxError);
}
runtest(test);