diff --git a/lib/parse.js b/lib/parse.js index 6298419b..f13d9bc4 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -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 == ")") { diff --git a/test/compress/harmony.js b/test/compress/harmony.js index 50fb47af..8093384b 100644 --- a/test/compress/harmony.js +++ b/test/compress/harmony.js @@ -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; } - } } \ No newline at end of file diff --git a/test/compress/yield.js b/test/compress/yield.js new file mode 100644 index 00000000..9fe9f6c8 --- /dev/null +++ b/test/compress/yield.js @@ -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 {} + } +} \ No newline at end of file diff --git a/test/mocha/arrow.js b/test/mocha/arrow.js index 19bbb429..a02286c2 100644 --- a/test/mocha/arrow.js +++ b/test/mocha/arrow.js @@ -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); } }); -}); \ No newline at end of file +}); diff --git a/test/mocha/class.js b/test/mocha/class.js index e0b1fbd1..0df67e6a 100644 --- a/test/mocha/class.js +++ b/test/mocha/class.js @@ -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); } }); -}); \ No newline at end of file +}); diff --git a/test/mocha/function.js b/test/mocha/function.js index cdfb5cee..34fc70b3 100644 --- a/test/mocha/function.js +++ b/test/mocha/function.js @@ -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); } }); -}); \ No newline at end of file +}); diff --git a/test/mocha/yield.js b/test/mocha/yield.js index b105ef60..1211ea9c 100644 --- a/test/mocha/yield.js +++ b/test/mocha/yield.js @@ -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]); + } + }); }); \ No newline at end of file