#430: fix reversions with legacy generators, restore/new tests

This commit is contained in:
Cameron Kaiser 2017-08-25 16:22:18 -07:00
parent a9e9d0bb5f
commit a05b152eef
16 changed files with 190 additions and 12 deletions

View File

@ -5910,7 +5910,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
if (matched) {
iflags = JSITER_FOREACH;
isForEach = true;
addTelemetry(JSCompartment::DeprecatedForEach);
//addTelemetry(JSCompartment::DeprecatedForEach);
if (versionNumber() < JSVERSION_LATEST) {
if (!report(ParseWarning, pc->sc->strict(), null(), JSMSG_DEPRECATED_FOR_EACH))
return null();
@ -8392,7 +8392,7 @@ Parser<FullParseHandler>::legacyComprehensionTail(ParseNode* bodyExpr, unsigned
return null();
if (matched) {
pn2->pn_iflags |= JSITER_FOREACH;
addTelemetry(JSCompartment::DeprecatedForEach);
//addTelemetry(JSCompartment::DeprecatedForEach);
if (versionNumber() < JSVERSION_LATEST) {
if (!report(ParseWarning, pc->sc->strict(), pn2, JSMSG_DEPRECATED_FOR_EACH))
return null();
@ -10084,16 +10084,6 @@ Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling
}
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
PossibleError* possibleError)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
}
// Legacy generator comprehensions can appear anywhere an expression is
// enclosed in parentheses, even if those parentheses are part of statement
// syntax or a function call:
@ -10111,6 +10101,44 @@ Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHan
// sum(x*x for (x in y)); // ok
// sum(for (x of y) x*x); // SyntaxError: needs more parens
//
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,
TripledotHandling tripledotHandling,
PossibleError* possibleError)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
uint32_t begin = pos().begin;
uint32_t startYieldOffset = pc->lastYieldOffset;
Node pn = expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked);
if (!pn)
return null();
#if JS_HAS_GENERATOR_EXPRS
bool matched;
if (!tokenStream.matchToken(&matched, TOK_FOR))
return null();
if (matched) {
if (pc->lastYieldOffset != startYieldOffset) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
JSMSG_BAD_GENEXP_BODY, js_yield_str);
return null();
}
if (handler.isUnparenthesizedCommaExpression(pn)) {
report(ParseError, false, null(), JSMSG_BAD_GENERATOR_SYNTAX);
return null();
}
pn = legacyGeneratorExpr(pn);
if (!pn)
return null();
handler.setBeginPosition(pn, begin);
}
#endif /* JS_HAS_GENERATOR_EXPRS */
return pn;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling,

View File

@ -0,0 +1,4 @@
function f1(g=((function () { return 4; }) for (x of [1]))) {
return g.next()();
}
assertEq(f1(), 4);

View File

@ -16,6 +16,7 @@ assertEq(f3()()(), true);
// These should be okay.
function f4(f=(function () { with (Object) {} }), g=(function () { "use strict"; })) {}
function f5(g=(function () { "use strict"; }), f=(function () { with (Object) {} })) {}
function f6(f=(function () { return (x for (y in (function g() {}))); })) {}
assertThrowsInstanceOf(function () {
eval("'use strict'; function f(a=delete x) { }");

View File

@ -0,0 +1,7 @@
// No 'arguments' binding in genexprs at toplevel.
load(libdir + "asserts.js");
delete this.arguments; // it is defined in the shell
var iter = (arguments for (x of [1]));
assertThrowsInstanceOf(() => iter.next(), ReferenceError);

View File

@ -0,0 +1,4 @@
// 'arguments' is lexically scoped in genexprs at toplevel.
var arguments = 8;
assertEq((arguments for (x of [1])).next(), 8);

View File

@ -0,0 +1,6 @@
// 'arguments' is lexically scoped in genexpr in toplevel let-block.
{
let arguments = [];
assertEq((arguments for (p in {a: 1})).next(), arguments);
}

View File

@ -0,0 +1,7 @@
// 'arguments' is lexically scoped in genexpr in function.
function f() {
assertEq((arguments for (x of [0])).next(),
(arguments for (y of [1])).next());
}
f();

View File

@ -0,0 +1,8 @@
// 'arguments' binding can be closed over and outlives the function activation.
function f() {
return (arguments for (x of [1]));
}
var args = f("ponies").next();
assertEq(args[0], "ponies");

View File

@ -0,0 +1,12 @@
// 'arguments' works in nested genexprs.
function f() {
return ((((((arguments for (u of [0]))
for (v of [1]))
for (w of [2]))
for (x of [3]))
for (y of [4]))
for (z of [5]));
}
var args = f("ponies").next().next().next().next().next().next();
assertEq(args[0], "ponies");

View File

@ -0,0 +1,10 @@
// 'this' in generator-expression in strict-mode toplevel
// is the same as global 'this'.
"use strict";
var it1 = (this for (x of [0]));
assertEq(it1.next(), this);
var it2 = (this for (x of (this for (y of (this for (z of [0]))))));
assertEq(it2.next(), this);

View File

@ -0,0 +1,11 @@
// 'this' in escaping generator-expression in a method
// is the same as 'this' in the enclosing method.
var obj = {
f: function () {
assertEq(this, obj);
return (this for (x of [0]));
}
};
assertEq(obj.f().next(), obj);

View File

@ -0,0 +1,11 @@
// 'this' in escaping generator-expression in a method
// is the same as 'this' in the enclosing method
// even if the method does not otherwise use 'this'.
var obj = {
f: function () {
return (this for (x of [0]));
}
};
assertEq(obj.f().next(), obj);

View File

@ -0,0 +1,13 @@
// 'this' in a generator-expression non-strict function produces the expected
// object.
Number.prototype.iters = function () {
return [(this for (x of [0])),
(this for (y of [0]))];
};
var [a, b] = (3).iters();
var three = a.next();
assertEq(Object.prototype.toString.call(three), '[object Number]');
assertEq(+three, 3);
assertEq(b.next(), three);

View File

@ -191,6 +191,18 @@ test('try {for (let x in eval("throw x")) {}} catch (e) {return e;}', undefined,
test('try {for each (let x in x) {eval("throw x");}} catch (e) {return e;}', ['ponies'], undefined);
test('for each (let {x: y, y: x} in [{x: x, y: x}]) {return y;}', undefined, undefined);
// genexps
test('return (i for (i in x)).next();', {ponies:true});
test('return (eval("i") for (i in x)).next();', {ponies:true});
test('return (eval("i") for (i in eval("x"))).next();', {ponies:true});
test('try {return (eval("throw i") for (i in x)).next();} catch (e) {return e;}', {ponies:true});
// array comprehension
test('return [i for (i in x)][0];', {ponies:true});
test('return [eval("i") for (i in x)][0];', {ponies:true});
test('return [eval("i") for (i in eval("x"))][0];', {ponies:true});
test('try {return [eval("throw i") for (i in x)][0];} catch (e) {return e;}', {ponies:true});
// don't forget about switch craziness
test('var y = 3;switch (function () {return eval("y");}()) {case 3:let y;return x;default:;}');
test('switch (x) {case 3:let y;return 3;case 4:let z;return 4;default:return x;}');

View File

@ -0,0 +1,14 @@
// array comprehensions
var data = "abcdefg";
var hexString = [
data.charCodeAt(i) for (i in data) if (data.charCodeAt(i) < 100)
].join(",");
assertEq(hexString, "97,98,99");
// generator comprehensions
var it = [1, 2, 3, 5, 8, 10, 13];
var w = (i*2 for (i of it) if (i & 1));
assertEq(w.next(), 2);
assertEq(w.next(), 6);
assertEq(w.next(), 10);
assertEq(w.next(), 26);

View File

@ -0,0 +1,30 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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/. */
//-----------------------------------------------------------------------------
var BUGNUMBER = 380237;
var summary = 'Decompilation of generator expressions';
var actual = '';
var expect = '';
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
var f = function() { g = (d for (d in [0])); g.next(); };
expect = 'function() { g = (d for (d in [0])); g.next(); }';
actual = f + '';
compareSource(expect, actual, summary);
exitFunc ('test');
}