Restrict yield outside generators in strict mode

* Move some yield/generic tests from compress/harmony.js to
  compress/yield.js
 * Adjust error messages to conform ecmascript standards
This commit is contained in:
Anthony Van de Gejuchte
2016-06-17 20:39:03 +02:00
committed by Richard van Velzen
parent 6b03b800b3
commit ca04508cd1
7 changed files with 208 additions and 123 deletions

View File

@@ -795,14 +795,14 @@ function parse($TEXT, options) {
function unexpected(token) {
if (token == null)
token = S.token;
token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
token_error(token, "SyntaxError: Unexpected token: " + token.type + " (" + token.value + ")");
};
function expect_token(type, val) {
if (is(type, val)) {
return next();
}
token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
token_error(S.token, "SyntaxError: Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
};
function expect(punc) { return expect_token("punc", punc); };
@@ -993,7 +993,7 @@ function parse($TEXT, options) {
var label = as_symbol(AST_Label);
if (label.name === "yield" && is_in_generator()) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors
croak("Yield cannot be used as label inside generators");
token_error(S.prev, "SyntaxError: Yield cannot be used as label inside generators");
}
if (find_if(function(l){ return l.name == label.name }, S.labels)) {
// ECMA-262, 12.12: An ECMAScript program is considered
@@ -1873,9 +1873,12 @@ function parse($TEXT, options) {
expect("]");
return ex;
} else unexpected();
case "num":
case "string":
case "name":
if (tmp.value === "yield" && S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "SyntaxError: Unexpected yield identifier inside strict mode");
}
case "string":
case "num":
case "operator":
case "keyword":
case "atom":
@@ -1915,6 +1918,9 @@ function parse($TEXT, options) {
if (!noerror) croak("Name expected");
return null;
}
if (is("name", "yield") && S.input.has_directive("use strict")) {
token_error(S.prev, "SyntaxError: Unexpected yield identifier inside strict mode");
}
var sym = _make_symbol(type);
next();
return sym;
@@ -2050,9 +2056,13 @@ function parse($TEXT, options) {
var maybe_assign = function(no_in) {
var start = S.token;
if (start.type == "name" && start.value == "yield" && is_in_generator()) {
next();
return _yield_expression();
if (start.type == "name" && start.value == "yield") {
if (is_in_generator()) {
next();
return _yield_expression();
} else if (S.input.has_directive("use strict")) {
token_error(S.token, "SyntaxError: Unexpected yield identifier inside strict mode")
}
}
if (start.type == "punc" && start.value == "(" && peek().value == ")") {

View File

@@ -449,110 +449,4 @@ regression_cannot_use_of: {
x.of;
foo(); /* Label statement missing? No prob. */
}
}
generators: {
input: {
function* fn() {};
}
expect_exact: "function*fn(){}"
}
generators_yield: {
input: {
function* fn() {
yield remote();
}
}
expect_exact: "function*fn(){yield remote()}"
}
generators_yield_assign: {
input: {
function* fn() {
var x = {};
x.prop = yield 5;
}
}
expect_exact: "function*fn(){var x={};x.prop=yield 5}"
}
generator_yield_undefined: {
input: {
function* fn() {
yield;
}
}
expect_exact: "function*fn(){yield}"
}
yield_optimize_expression: {
options = {
}
input: {
function* f1() { yield; }
function* f2() { yield undefined; }
function* f3() { yield null; }
function* f4() { yield* undefined; }
}
expect: {
function* f1() { yield }
function* f2() { yield; }
function* f3() { yield null; }
function* f4() { yield* void 0; }
}
}
yield_statements: {
input: {
function* fn() {
var a = (yield 1) + (yield 2);
var b = (yield 3) === (yield 4);
var c = (yield 5) << (yield 6);
var d = yield 7;
var e = (yield 8) ? yield 9 : yield 10;
var f = -(yield 11);
}
}
expect_exact: "function*fn(){var a=(yield 1)+(yield 2);var b=(yield 3)===(yield 4);var c=(yield 5)<<(yield 6);var d=yield 7;var e=(yield 8)?yield 9:yield 10;var f=-(yield 11)}"
}
yield_as_identifier_in_function_in_generator: {
input: {
var g = function*() {
function h() {
yield = 1;
}
};
}
expect: {
var g = function*() {
function h() {
yield = 1;
}
};
}
}
yield_before_punctuators: {
input: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} } // Added return to avoid {} drop
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
expect: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} }
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
}

142
test/compress/yield.js Normal file
View File

@@ -0,0 +1,142 @@
generators: {
input: {
function* fn() {};
}
expect_exact: "function*fn(){}"
}
generators_yield: {
input: {
function* fn() {
yield remote();
}
}
expect_exact: "function*fn(){yield remote()}"
}
generators_yield_assign: {
input: {
function* fn() {
var x = {};
x.prop = yield 5;
}
}
expect_exact: "function*fn(){var x={};x.prop=yield 5}"
}
generator_yield_undefined: {
input: {
function* fn() {
yield;
}
}
expect_exact: "function*fn(){yield}"
}
yield_optimize_expression: {
options = {
}
input: {
function* f1() { yield; }
function* f2() { yield undefined; }
function* f3() { yield null; }
function* f4() { yield* undefined; }
}
expect: {
function* f1() { yield }
function* f2() { yield; }
function* f3() { yield null; }
function* f4() { yield* void 0; }
}
}
yield_statements: {
input: {
function* fn() {
var a = (yield 1) + (yield 2);
var b = (yield 3) === (yield 4);
var c = (yield 5) << (yield 6);
var d = yield 7;
var e = (yield 8) ? yield 9 : yield 10;
var f = -(yield 11);
}
}
expect_exact: "function*fn(){var a=(yield 1)+(yield 2);var b=(yield 3)===(yield 4);var c=(yield 5)<<(yield 6);var d=yield 7;var e=(yield 8)?yield 9:yield 10;var f=-(yield 11)}"
}
yield_as_identifier_in_function_in_generator: {
input: {
var g = function*() {
function h() {
yield = 1;
}
};
}
expect: {
var g = function*() {
function h() {
yield = 1;
}
};
}
}
yield_before_punctuators: {
input: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} } // Added return to avoid {} drop
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
expect: {
iter = (function*() {
assignmentResult = [ x = yield ] = value;
})();
function* g1() { (yield) }
function* g2() { [yield] }
function* g3() { return {yield} }
function* g4() { yield, yield; }
function* g5() { (yield) ? yield : yield; }
}
}
yield_as_identifier_outside_strict_mode: {
input: {
import yield from "bar";
yield = 123;
while (true) {
yield:
for(;;) break yield;
foo();
}
while (true)
yield: for(;;) continue yield;
function yield(){}
function foo(...yield){}
try { new Error("") } catch (yield) {}
var yield = "foo";
class yield {}
}
expect: {
import yield from "bar";
yield = 123;
while (true) {
yield:
for(;;) break yield;
foo();
}
while (true)
yield: for(;;) continue yield;
function yield(){}
function foo(...yield){}
try { new Error("") } catch (yield) {}
var yield = "foo";
class yield {}
}
}

View File

@@ -15,11 +15,11 @@ describe("Arrow functions", function() {
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: expand (...)";
e.message === "SyntaxError: Unexpected token: expand (...)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
});
});

View File

@@ -16,11 +16,11 @@ describe("Class", function() {
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: expand (...)";
e.message === "SyntaxError: Unexpected token: expand (...)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
});
});

View File

@@ -20,11 +20,11 @@ describe("Function", function() {
}
var error = function(e) {
return e instanceof uglify.JS_Parse_Error &&
e.message === "Unexpected token: expand (...)";
e.message === "SyntaxError: Unexpected token: expand (...)";
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), error);
}
});
});
});

View File

@@ -15,7 +15,7 @@ describe("Yield", function() {
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Yield cannot be used as label inside generators";
e.message === "SyntaxError: Yield cannot be used as label inside generators";
}
assert.throws(test, expect);
});
@@ -27,7 +27,7 @@ describe("Yield", function() {
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Unexpected token: punc (;)";
e.message === "SyntaxError: Unexpected token: punc (;)";
}
assert.throws(test, expect);
});
@@ -39,7 +39,7 @@ describe("Yield", function() {
}
var expect = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "Unexpected token: operator (*)";
e.message === "SyntaxError: Unexpected token: operator (*)";
}
assert.throws(test, expect);
});
@@ -64,4 +64,43 @@ describe("Yield", function() {
"function*f(){yield,yield,yield*void 0,yield}"
);
});
it("Should not allow yield to be used as symbol, identifier or property outside generators in strict mode", function() {
var tests = [
// Fail as as_symbol
'"use strict"; import yield from "bar";',
'"use strict"; yield = 123;',
'"use strict"; yield: "123";',
'"use strict"; for(;;){break yield;}',
'"use strict"; for(;;){continue yield;}',
'"use strict"; function yield(){}',
'"use strict"; function foo(...yield){}',
'"use strict"; try { new Error("")} catch (yield) {}',
'"use strict"; var yield = "foo";',
'"use strict"; class yield {}',
// Fail as maybe_assign
'"use strict"; var foo = yield;',
'"use strict"; var foo = bar = yield',
// Fail as as_property_name
'"use strict"; var foo = {yield};',
'"use strict"; var bar = {yield: "foo"};'
];
var fail = function(e) {
return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected yield identifier inside strict mode";
}
var test = function(input) {
return function() {
UglifyJS.parse(input);
}
}
for (var i = 0; i < tests.length; i++) {
assert.throws(test(tests[i]), fail, tests[i]);
}
});
});