@@ -799,6 +799,10 @@ function OutputStream(options) {
|
|||||||
// a = yield 3
|
// a = yield 3
|
||||||
if (p instanceof AST_Binary && p.operator !== "=")
|
if (p instanceof AST_Binary && p.operator !== "=")
|
||||||
return true;
|
return true;
|
||||||
|
// (yield 1)()
|
||||||
|
// new (yield 1)()
|
||||||
|
if (p instanceof AST_Call && p.expression === this)
|
||||||
|
return true;
|
||||||
// (yield 1) ? yield 2 : yield 3
|
// (yield 1) ? yield 2 : yield 3
|
||||||
if (p instanceof AST_Conditional && p.condition === this)
|
if (p instanceof AST_Conditional && p.condition === this)
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
79
lib/parse.js
79
lib/parse.js
@@ -1168,10 +1168,6 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function labeled_statement() {
|
function labeled_statement() {
|
||||||
var label = as_symbol(AST_Label);
|
var label = as_symbol(AST_Label);
|
||||||
if (label.name === "yield" && is_in_generator()) {
|
|
||||||
// Ecma-262, 12.1.1 Static Semantics: Early Errors
|
|
||||||
token_error(S.prev, "Yield cannot be used as label inside generators");
|
|
||||||
}
|
|
||||||
if (label.name === "await" && is_in_async()) {
|
if (label.name === "await" && is_in_async()) {
|
||||||
token_error(S.prev, "await cannot be used as label inside async function");
|
token_error(S.prev, "await cannot be used as label inside async function");
|
||||||
}
|
}
|
||||||
@@ -1331,7 +1327,7 @@ function parse($TEXT, options) {
|
|||||||
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
|
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
|
||||||
unexpected(prev());
|
unexpected(prev());
|
||||||
|
|
||||||
var args = parameters();
|
var args = [];
|
||||||
var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
|
var body = _function_body(true, is_generator || is_generator_property, is_async, name, args);
|
||||||
return new ctor({
|
return new ctor({
|
||||||
start : args.start,
|
start : args.start,
|
||||||
@@ -1402,9 +1398,8 @@ function parse($TEXT, options) {
|
|||||||
return tracker;
|
return tracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parameters() {
|
function parameters(params) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var params = [];
|
|
||||||
var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
|
var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
|
||||||
|
|
||||||
expect("(");
|
expect("(");
|
||||||
@@ -1424,7 +1419,6 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
return params;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parameter(used_parameters, symbol_type) {
|
function parameter(used_parameters, symbol_type) {
|
||||||
@@ -1511,12 +1505,7 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
} else if (is("name")) {
|
} else if (is("name")) {
|
||||||
used_parameters.add_parameter(S.token);
|
used_parameters.add_parameter(S.token);
|
||||||
elements.push(new symbol_type({
|
elements.push(as_symbol(symbol_type));
|
||||||
start: S.token,
|
|
||||||
name: S.token.value,
|
|
||||||
end: S.token
|
|
||||||
}));
|
|
||||||
next();
|
|
||||||
} else {
|
} else {
|
||||||
croak("Invalid function parameter");
|
croak("Invalid function parameter");
|
||||||
}
|
}
|
||||||
@@ -1566,11 +1555,8 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
|
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
|
||||||
used_parameters.add_parameter(S.token);
|
used_parameters.add_parameter(S.token);
|
||||||
var value = new symbol_type({
|
var start = prev();
|
||||||
start: S.token,
|
var value = as_symbol(symbol_type);
|
||||||
name: S.token.value,
|
|
||||||
end: S.token,
|
|
||||||
});
|
|
||||||
if (is_expand) {
|
if (is_expand) {
|
||||||
elements.push(new AST_Expansion({
|
elements.push(new AST_Expansion({
|
||||||
start: expand_token,
|
start: expand_token,
|
||||||
@@ -1579,13 +1565,12 @@ function parse($TEXT, options) {
|
|||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
elements.push(new AST_ObjectKeyVal({
|
elements.push(new AST_ObjectKeyVal({
|
||||||
start: prev(),
|
start: start,
|
||||||
key: S.token.value,
|
key: value.name,
|
||||||
value: value,
|
value: value,
|
||||||
end: value.end,
|
end: value.end,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
next();
|
|
||||||
} else if (is("punc", "}")) {
|
} else if (is("punc", "}")) {
|
||||||
continue; // Allow trailing hole
|
continue; // Allow trailing hole
|
||||||
} else {
|
} else {
|
||||||
@@ -1642,12 +1627,7 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
} else if (is("name")) {
|
} else if (is("name")) {
|
||||||
used_parameters.add_parameter(S.token);
|
used_parameters.add_parameter(S.token);
|
||||||
next();
|
return as_symbol(symbol_type);
|
||||||
return new symbol_type({
|
|
||||||
start: prev(),
|
|
||||||
name: prev().value,
|
|
||||||
end: prev()
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
croak("Invalid function parameter");
|
croak("Invalid function parameter");
|
||||||
}
|
}
|
||||||
@@ -1701,6 +1681,7 @@ function parse($TEXT, options) {
|
|||||||
S.in_generator = S.in_function;
|
S.in_generator = S.in_function;
|
||||||
if (is_async)
|
if (is_async)
|
||||||
S.in_async = S.in_function;
|
S.in_async = S.in_function;
|
||||||
|
if (args) parameters(args);
|
||||||
if (block)
|
if (block)
|
||||||
S.in_directives = true;
|
S.in_directives = true;
|
||||||
S.in_loop = 0;
|
S.in_loop = 0;
|
||||||
@@ -1708,10 +1689,8 @@ function parse($TEXT, options) {
|
|||||||
if (block) {
|
if (block) {
|
||||||
S.input.push_directives_stack();
|
S.input.push_directives_stack();
|
||||||
var a = block_();
|
var a = block_();
|
||||||
if (S.input.has_directive("use strict")) {
|
if (name) _verify_symbol(name);
|
||||||
if (name) strict_verify_symbol(name);
|
if (args) args.forEach(_verify_symbol);
|
||||||
if (args) args.forEach(strict_verify_symbol);
|
|
||||||
}
|
|
||||||
S.input.pop_directives_stack();
|
S.input.pop_directives_stack();
|
||||||
} else {
|
} else {
|
||||||
var a = expression(false);
|
var a = expression(false);
|
||||||
@@ -2593,9 +2572,14 @@ function parse($TEXT, options) {
|
|||||||
unexpected(tmp);
|
unexpected(tmp);
|
||||||
}
|
}
|
||||||
case "name":
|
case "name":
|
||||||
if (tmp.value == "yield" && !is_token(peek(), "punc", ":") && !is_token(peek(), "punc", "(")
|
if (tmp.value == "yield") {
|
||||||
&& S.input.has_directive("use strict") && !is_in_generator()) {
|
if (is_in_generator()) {
|
||||||
token_error(tmp, "Unexpected yield identifier inside strict mode");
|
token_error(tmp, "Yield cannot be used as identifier inside generators");
|
||||||
|
} else if (!is_token(peek(), "punc", ":")
|
||||||
|
&& !is_token(peek(), "punc", "(")
|
||||||
|
&& S.input.has_directive("use strict")) {
|
||||||
|
token_error(tmp, "Unexpected yield identifier inside strict mode");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case "string":
|
case "string":
|
||||||
case "num":
|
case "num":
|
||||||
@@ -2626,9 +2610,19 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function strict_verify_symbol(sym) {
|
function _verify_symbol(sym) {
|
||||||
if (sym.name == "arguments" || sym.name == "eval")
|
var name = sym.name;
|
||||||
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
|
if (is_in_generator() && name == "yield") {
|
||||||
|
token_error(sym.start, "Yield cannot be used as identifier inside generators");
|
||||||
|
}
|
||||||
|
if (S.input.has_directive("use strict")) {
|
||||||
|
if (name == "yield") {
|
||||||
|
token_error(sym.start, "Unexpected yield identifier inside strict mode");
|
||||||
|
}
|
||||||
|
if (sym instanceof AST_SymbolDeclaration && (name == "arguments" || name == "eval")) {
|
||||||
|
token_error(sym.start, "Unexpected " + name + " in strict mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function as_symbol(type, noerror) {
|
function as_symbol(type, noerror) {
|
||||||
@@ -2636,13 +2630,8 @@ function parse($TEXT, options) {
|
|||||||
if (!noerror) croak("Name expected");
|
if (!noerror) croak("Name expected");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (is("name", "yield") && S.input.has_directive("use strict")) {
|
|
||||||
token_error(S.prev, "Unexpected yield identifier inside strict mode");
|
|
||||||
}
|
|
||||||
var sym = _make_symbol(type);
|
var sym = _make_symbol(type);
|
||||||
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
|
_verify_symbol(sym);
|
||||||
strict_verify_symbol(sym);
|
|
||||||
}
|
|
||||||
next();
|
next();
|
||||||
return sym;
|
return sym;
|
||||||
};
|
};
|
||||||
@@ -2870,7 +2859,7 @@ function parse($TEXT, options) {
|
|||||||
next();
|
next();
|
||||||
return _yield_expression();
|
return _yield_expression();
|
||||||
} else if (S.input.has_directive("use strict")) {
|
} else if (S.input.has_directive("use strict")) {
|
||||||
token_error(S.token, "Unexpected yield identifier inside strict mode")
|
token_error(S.token, "Unexpected yield identifier inside strict mode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,8 @@ yield_before_punctuators: {
|
|||||||
})();
|
})();
|
||||||
function* g1() { (yield) }
|
function* g1() { (yield) }
|
||||||
function* g2() { [yield] }
|
function* g2() { [yield] }
|
||||||
function* g3() { return {yield} } // Added return to avoid {} drop
|
function* g3() { yield, yield; }
|
||||||
function* g4() { yield, yield; }
|
function* g4() { (yield) ? yield : yield; }
|
||||||
function* g5() { (yield) ? yield : yield; }
|
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
iter = (function*() {
|
iter = (function*() {
|
||||||
@@ -98,9 +97,8 @@ yield_before_punctuators: {
|
|||||||
})();
|
})();
|
||||||
function* g1() { (yield) }
|
function* g1() { (yield) }
|
||||||
function* g2() { [yield] }
|
function* g2() { [yield] }
|
||||||
function* g3() { return {yield} }
|
function* g3() { yield, yield; }
|
||||||
function* g4() { yield, yield; }
|
function* g4() { (yield) ? yield : yield; }
|
||||||
function* g5() { (yield) ? yield : yield; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,3 +197,21 @@ yield_as_ES5_property: {
|
|||||||
expect_exact: '"use strict";console.log({yield:42}.yield);'
|
expect_exact: '"use strict";console.log({yield:42}.yield);'
|
||||||
expect_stdout: "42"
|
expect_stdout: "42"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_2689: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function* y() {
|
||||||
|
var t = yield x();
|
||||||
|
return new t();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function* y() {
|
||||||
|
return new (yield x());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,18 +8,6 @@ describe("Yield", function() {
|
|||||||
assert.strictEqual(result.code, 'function*foo(e){return yield 1,yield 2,3}');
|
assert.strictEqual(result.code, 'function*foo(e){return yield 1,yield 2,3}');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not allow yield as labelIdentifier within generators", function() {
|
|
||||||
var js = "function* g() {yield: 1}"
|
|
||||||
var test = function() {
|
|
||||||
UglifyJS.parse(js);
|
|
||||||
}
|
|
||||||
var expect = function(e) {
|
|
||||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
|
||||||
e.message === "Yield cannot be used as label inside generators";
|
|
||||||
}
|
|
||||||
assert.throws(test, expect);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Should not allow yield* followed by a semicolon in generators", function() {
|
it("Should not allow yield* followed by a semicolon in generators", function() {
|
||||||
var js = "function* test() {yield*\n;}";
|
var js = "function* test() {yield*\n;}";
|
||||||
var test = function() {
|
var test = function() {
|
||||||
@@ -65,42 +53,68 @@ describe("Yield", function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var identifiers = [
|
||||||
|
// Fail in as_symbol
|
||||||
|
'import yield from "bar";',
|
||||||
|
'yield = 123;',
|
||||||
|
'yield: "123";',
|
||||||
|
'for(;;){break yield;}',
|
||||||
|
'for(;;){continue yield;}',
|
||||||
|
'function yield(){}',
|
||||||
|
'try { new Error("")} catch (yield) {}',
|
||||||
|
'var yield = "foo";',
|
||||||
|
'class yield {}',
|
||||||
|
// Fail in as_property_name
|
||||||
|
'var foo = {yield};',
|
||||||
|
];
|
||||||
|
|
||||||
it("Should not allow yield to be used as symbol, identifier or shorthand property outside generators in strict mode", function() {
|
it("Should not allow yield to be used as symbol, identifier or shorthand property outside generators in strict mode", function() {
|
||||||
var tests = [
|
function fail(e) {
|
||||||
// Fail in 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 in maybe_assign
|
|
||||||
'"use strict"; var foo = yield;',
|
|
||||||
'"use strict"; var foo = bar = yield',
|
|
||||||
|
|
||||||
// Fail in as_property_name
|
|
||||||
'"use strict"; var foo = {yield};',
|
|
||||||
];
|
|
||||||
|
|
||||||
var fail = function(e) {
|
|
||||||
return e instanceof UglifyJS.JS_Parse_Error &&
|
return e instanceof UglifyJS.JS_Parse_Error &&
|
||||||
/^Unexpected yield identifier (?:as parameter )?inside strict mode/.test(e.message);
|
/^Unexpected yield identifier (?:as parameter )?inside strict mode/.test(e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
var test = function(input) {
|
function test(input) {
|
||||||
return function() {
|
return function() {
|
||||||
UglifyJS.parse(input);
|
UglifyJS.parse(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < tests.length; i++) {
|
identifiers.concat([
|
||||||
assert.throws(test(tests[i]), fail, tests[i]);
|
// Fail in as_symbol
|
||||||
|
"function foo(...yield){}",
|
||||||
|
// Fail in maybe_assign
|
||||||
|
'var foo = yield;',
|
||||||
|
'var foo = bar = yield',
|
||||||
|
]).map(function(code) {
|
||||||
|
return '"use strict"; ' + code;
|
||||||
|
}).forEach(function(code) {
|
||||||
|
assert.throws(test(code), fail, code);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should not allow yield to be used as symbol, identifier or shorthand property inside generators", function() {
|
||||||
|
function fail(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error && [
|
||||||
|
"Unexpected token: operator (=)",
|
||||||
|
"Yield cannot be used as identifier inside generators",
|
||||||
|
].indexOf(e.message) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test(input) {
|
||||||
|
return function() {
|
||||||
|
UglifyJS.parse(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
identifiers.map(function(code) {
|
||||||
|
return "function* f() { " + code + " }";
|
||||||
|
}).concat([
|
||||||
|
// Fail in as_symbol
|
||||||
|
"function* f(yield) {}",
|
||||||
|
]).forEach(function(code) {
|
||||||
|
assert.throws(test(code), fail, code);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should allow yield to be used as class/object property name", function() {
|
it("Should allow yield to be used as class/object property name", function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user