Merge remote-tracking branch 'refs/remotes/mishoo/master'

This commit is contained in:
pborunda 2017-02-27 15:28:29 -07:00
commit 9e8da9ed85
24 changed files with 370 additions and 148 deletions

View File

@ -10,6 +10,9 @@ There's also an
[in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox, [in-browser online demo](http://lisperator.net/uglifyjs/#demo) (for Firefox,
Chrome and probably Safari). Chrome and probably Safari).
Note: release versions of `uglify-js` only support ECMAScript 5 (ES5). If you wish to minify
ES2015+ (ES6+) code then please use the [harmony](#harmony) development branch.
Install Install
------- -------
@ -969,3 +972,20 @@ The `source_map_options` (optional) can contain the following properties:
[codegen]: http://lisperator.net/uglifyjs/codegen [codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress [compressor]: http://lisperator.net/uglifyjs/compress
[parser]: http://lisperator.net/uglifyjs/parser [parser]: http://lisperator.net/uglifyjs/parser
#### Harmony
If you wish to use the experimental [harmony](https://github.com/mishoo/UglifyJS2/commits/harmony)
branch to minify ES2015+ (ES6+) code please use the following in your `package.json` file:
```
"uglify-js": "git+https://github.com/mishoo/UglifyJS2.git#harmony"
```
or to directly install the experimental harmony version of uglify:
```
npm install --save-dev uglify-js@github:mishoo/UglifyJS2#harmony
```
See [#448](https://github.com/mishoo/UglifyJS2/issues/448) for additional details.

View File

@ -364,7 +364,21 @@ async.eachLimit(files, 1, function (file, cb) {
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.JS_Parse_Error) { if (ex instanceof UglifyJS.JS_Parse_Error) {
print_error("Parse error at " + file + ":" + ex.line + "," + ex.col); print_error("Parse error at " + file + ":" + ex.line + "," + ex.col);
print_error(ex.message); var col = ex.col;
var line = code.split(/\r?\n/)[ex.line - (col ? 1 : 2)];
if (line) {
if (col > 40) {
line = line.slice(col - 40);
col = 40;
}
if (col) {
print_error(line.slice(0, 80));
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
} else {
print_error(line.slice(-40));
print_error(line.slice(-40).replace(/\S/g, " ") + "^");
}
}
print_error(ex.stack); print_error(ex.stack);
process.exit(1); process.exit(1);
} }
@ -390,7 +404,7 @@ async.eachLimit(files, 1, function (file, cb) {
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS); var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) { } catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) { if (ex instanceof UglifyJS.DefaultsError) {
print_error(ex.msg); print_error(ex.message);
print_error("Supported options:"); print_error("Supported options:");
print_error(sys.inspect(ex.defs)); print_error(sys.inspect(ex.defs));
process.exit(1); process.exit(1);

View File

@ -1376,9 +1376,7 @@ merge(Compressor.prototype, {
&& (comments = this.start.comments_before) && (comments = this.start.comments_before)
&& comments.length && comments.length
&& /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) { && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start); pure = last_comment;
last_comment.value = last_comment.value.replace(/[@#]__PURE__/g, ' ');
pure = true;
} }
return this.pure = pure; return this.pure = pure;
}); });
@ -1677,8 +1675,7 @@ merge(Compressor.prototype, {
line : def.name.start.line, line : def.name.start.line,
col : def.name.start.col col : def.name.start.col
}; };
if (def.value && def.value.has_side_effects(compressor)) { if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) {
def._unused_side_effects = true;
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
return true; return true;
} }
@ -1698,7 +1695,7 @@ merge(Compressor.prototype, {
for (var i = 0; i < def.length;) { for (var i = 0; i < def.length;) {
var x = def[i]; var x = def[i];
if (x._unused_side_effects) { if (x._unused_side_effects) {
side_effects.push(x.value); side_effects.push(x._unused_side_effects);
def.splice(i, 1); def.splice(i, 1);
} else { } else {
if (side_effects.length > 0) { if (side_effects.length > 0) {
@ -1928,6 +1925,10 @@ merge(Compressor.prototype, {
def(AST_This, return_null); def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){ def(AST_Call, function(compressor, first_in_statement){
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this; if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
if (this.pure) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
}
var args = trim(this.args, compressor, first_in_statement); var args = trim(this.args, compressor, first_in_statement);
return args && AST_Seq.from_array(args); return args && AST_Seq.from_array(args);
}); });
@ -1979,8 +1980,17 @@ merge(Compressor.prototype, {
case "typeof": case "typeof":
if (this.expression instanceof AST_SymbolRef) return null; if (this.expression instanceof AST_SymbolRef) return null;
default: default:
if (first_in_statement && is_iife_call(this.expression)) return this; var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
return this.expression.drop_side_effect_free(compressor, first_in_statement); if (first_in_statement
&& this instanceof AST_UnaryPrefix
&& is_iife_call(expression)) {
if (expression === this.expression && this.operator.length === 1) return this;
return make_node(AST_UnaryPrefix, this, {
operator: this.operator.length === 1 ? this.operator : "!",
expression: expression
});
}
return expression;
} }
}); });
def(AST_SymbolRef, function() { def(AST_SymbolRef, function() {
@ -2028,10 +2038,6 @@ merge(Compressor.prototype, {
OPT(AST_SimpleStatement, function(self, compressor){ OPT(AST_SimpleStatement, function(self, compressor){
if (compressor.option("side_effects")) { if (compressor.option("side_effects")) {
var body = self.body; var body = self.body;
if (!body.has_side_effects(compressor)) {
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
return make_node(AST_EmptyStatement, self);
}
var node = body.drop_side_effect_free(compressor, true); var node = body.drop_side_effect_free(compressor, true);
if (!node) { if (!node) {
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
@ -2687,17 +2693,10 @@ merge(Compressor.prototype, {
} }
} }
if (!self.car.has_side_effects(compressor) if (!self.car.has_side_effects(compressor)
&& !self.cdr.has_side_effects(compressor)
&& self.car.equivalent_to(self.cdr)) { && self.car.equivalent_to(self.cdr)) {
return self.car; return self.car;
} }
} }
if (self.cdr instanceof AST_UnaryPrefix
&& self.cdr.operator == "void"
&& !self.cdr.expression.has_side_effects(compressor)) {
self.cdr.expression = self.car;
return self.cdr;
}
if (self.cdr instanceof AST_Undefined) { if (self.cdr instanceof AST_Undefined) {
return make_node(AST_UnaryPrefix, self, { return make_node(AST_UnaryPrefix, self, {
operator : "void", operator : "void",
@ -2726,8 +2725,20 @@ merge(Compressor.prototype, {
}); });
OPT(AST_UnaryPrefix, function(self, compressor){ OPT(AST_UnaryPrefix, function(self, compressor){
self = self.lift_sequences(compressor); var seq = self.lift_sequences(compressor);
if (seq !== self) {
return seq;
}
var e = self.expression; var e = self.expression;
if (compressor.option("side_effects") && self.operator == "void") {
e = e.drop_side_effect_free(compressor);
if (e) {
self.expression = e;
return self;
} else {
return make_node(AST_Undefined, self);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
switch (self.operator) { switch (self.operator) {
case "!": case "!":
@ -2744,13 +2755,10 @@ merge(Compressor.prototype, {
// typeof always returns a non-empty string, thus it's // typeof always returns a non-empty string, thus it's
// always true in booleans // always true in booleans
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
if (self.expression.has_side_effects(compressor)) {
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.expression, car: e,
cdr: make_node(AST_True, self) cdr: make_node(AST_True, self)
}); }).optimize(compressor);
}
return make_node(AST_True, self);
} }
} }
return self.evaluate(compressor)[0]; return self.evaluate(compressor)[0];
@ -2880,14 +2888,11 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) { if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
if (self.left.has_side_effects(compressor)) {
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
cdr: make_node(AST_False) cdr: make_node(AST_False)
}).optimize(compressor); }).optimize(compressor);
} }
return make_node(AST_False, self);
}
if (ll.length > 1 && ll[1]) { if (ll.length > 1 && ll[1]) {
return rr[0]; return rr[0];
} }
@ -2900,14 +2905,11 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) { if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
if (self.left.has_side_effects(compressor)) {
return make_node(AST_Seq, self, { return make_node(AST_Seq, self, {
car: self.left, car: self.left,
cdr: make_node(AST_True) cdr: make_node(AST_True, self)
}).optimize(compressor); }).optimize(compressor);
} }
return make_node(AST_True, self);
}
if (ll.length > 1 && !ll[1]) { if (ll.length > 1 && !ll[1]) {
return rr[0]; return rr[0];
} }
@ -2918,10 +2920,19 @@ merge(Compressor.prototype, {
case "+": case "+":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1] && !self.right.has_side_effects(compressor)) || if (ll.length > 1 && ll[0] instanceof AST_String && ll[1]) {
(rr.length > 1 && rr[0] instanceof AST_String && rr[1] && !self.left.has_side_effects(compressor))) {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_True, self); return make_node(AST_Seq, self, {
car: self.right,
cdr: make_node(AST_True, self)
}).optimize(compressor);
}
if (rr.length > 1 && rr[0] instanceof AST_String && rr[1]) {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, {
car: self.left,
cdr: make_node(AST_True, self)
}).optimize(compressor);
} }
break; break;
} }
@ -3171,7 +3182,8 @@ merge(Compressor.prototype, {
&& alternative instanceof AST_Assign && alternative instanceof AST_Assign
&& consequent.operator == alternative.operator && consequent.operator == alternative.operator
&& consequent.left.equivalent_to(alternative.left) && consequent.left.equivalent_to(alternative.left)
&& !consequent.left.has_side_effects(compressor) && (!consequent.left.has_side_effects(compressor)
|| !self.condition.has_side_effects(compressor))
) { ) {
/* /*
* Stuff like this: * Stuff like this:
@ -3189,18 +3201,13 @@ merge(Compressor.prototype, {
}) })
}); });
} }
// x ? y(a) : y(b) --> y(x ? a : b)
if (consequent instanceof AST_Call if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE && alternative.TYPE === consequent.TYPE
&& consequent.args.length == alternative.args.length && consequent.args.length == 1
&& !consequent.expression.has_side_effects(compressor) && alternative.args.length == 1
&& consequent.expression.equivalent_to(alternative.expression)) { && consequent.expression.equivalent_to(alternative.expression)
if (consequent.args.length == 0) { && !consequent.expression.has_side_effects(compressor)) {
return make_node(AST_Seq, self, {
car: self.condition,
cdr: consequent
});
}
if (consequent.args.length == 1) {
consequent.args[0] = make_node(AST_Conditional, self, { consequent.args[0] = make_node(AST_Conditional, self, {
condition: self.condition, condition: self.condition,
consequent: consequent.args[0], consequent: consequent.args[0],
@ -3208,7 +3215,6 @@ merge(Compressor.prototype, {
}); });
return consequent; return consequent;
} }
}
// x?y?z:a:a --> x&&y?z:a // x?y?z:a:a --> x&&y?z:a
if (consequent instanceof AST_Conditional if (consequent instanceof AST_Conditional
&& consequent.alternative.equivalent_to(alternative)) { && consequent.alternative.equivalent_to(alternative)) {
@ -3222,16 +3228,12 @@ merge(Compressor.prototype, {
alternative: alternative alternative: alternative
}); });
} }
// y?1:1 --> 1 // x ? y : y --> x, y
if (consequent.is_constant() if (consequent.equivalent_to(alternative)) {
&& alternative.is_constant() return make_node(AST_Seq, self, {
&& consequent.equivalent_to(alternative)) { car: self.condition,
var consequent_value = consequent.evaluate(compressor)[0]; cdr: consequent
if (self.condition.has_side_effects(compressor)) { }).optimize(compressor);
return AST_Seq.from_array([self.condition, consequent_value]);
} else {
return consequent_value;
}
} }
if (is_true(self.consequent)) { if (is_true(self.consequent)) {
@ -3390,8 +3392,12 @@ merge(Compressor.prototype, {
}); });
function literals_in_boolean_context(self, compressor) { function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
return make_node(AST_True, self); var best = first_in_statement(compressor) ? best_of_statement : best_of;
return best(self, make_node(AST_Seq, self, {
car: self,
cdr: make_node(AST_True, self)
}).optimize(compressor));
} }
return self; return self;
}; };

View File

@ -195,12 +195,11 @@ function JS_Parse_Error(message, filename, line, col, pos) {
this.line = line; this.line = line;
this.col = col; this.col = col;
this.pos = pos; this.pos = pos;
this.stack = new Error().stack;
};
JS_Parse_Error.prototype.toString = function() {
return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
}; };
JS_Parse_Error.prototype = Object.create(Error.prototype);
JS_Parse_Error.prototype.constructor = JS_Parse_Error;
JS_Parse_Error.prototype.name = "SyntaxError";
configure_error_stack(JS_Parse_Error);
function js_error(message, filename, line, col, pos) { function js_error(message, filename, line, col, pos) {
throw new JS_Parse_Error(message, filename, line, col, pos); throw new JS_Parse_Error(message, filename, line, col, pos);
@ -350,13 +349,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
}); });
if (prefix) num = prefix + num; if (prefix) num = prefix + num;
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) { if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
parse_error("SyntaxError: Legacy octal literals are not allowed in strict mode"); parse_error("Legacy octal literals are not allowed in strict mode");
} }
var valid = parse_js_number(num); var valid = parse_js_number(num);
if (!isNaN(valid)) { if (!isNaN(valid)) {
return token("num", valid); return token("num", valid);
} else { } else {
parse_error("SyntaxError: Invalid syntax: " + num); parse_error("Invalid syntax: " + num);
} }
}; };
@ -395,7 +394,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
// Parse // Parse
if (ch === "0") return "\0"; if (ch === "0") return "\0";
if (ch.length > 0 && next_token.has_directive("use strict")) if (ch.length > 0 && next_token.has_directive("use strict"))
parse_error("SyntaxError: Legacy octal escape sequences are not allowed in strict mode"); parse_error("Legacy octal escape sequences are not allowed in strict mode");
return String.fromCharCode(parseInt(ch, 8)); return String.fromCharCode(parseInt(ch, 8));
} }
@ -404,18 +403,18 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
for (; n > 0; --n) { for (; n > 0; --n) {
var digit = parseInt(next(true), 16); var digit = parseInt(next(true), 16);
if (isNaN(digit)) if (isNaN(digit))
parse_error("SyntaxError: Invalid hex-character pattern in string"); parse_error("Invalid hex-character pattern in string");
num = (num << 4) | digit; num = (num << 4) | digit;
} }
return num; return num;
}; };
var read_string = with_eof_error("SyntaxError: Unterminated string constant", function(quote_char){ var read_string = with_eof_error("Unterminated string constant", function(quote_char){
var quote = next(), ret = ""; var quote = next(), ret = "";
for (;;) { for (;;) {
var ch = next(true, true); var ch = next(true, true);
if (ch == "\\") ch = read_escaped_char(true); if (ch == "\\") ch = read_escaped_char(true);
else if (NEWLINE_CHARS(ch)) parse_error("SyntaxError: Unterminated string constant"); else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant");
else if (ch == quote) break; else if (ch == quote) break;
ret += ch; ret += ch;
} }
@ -440,7 +439,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return next_token; return next_token;
}; };
var skip_multiline_comment = with_eof_error("SyntaxError: Unterminated multiline comment", function(){ var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
var regex_allowed = S.regex_allowed; var regex_allowed = S.regex_allowed;
var i = find("*/", true); var i = find("*/", true);
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n'); var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
@ -460,9 +459,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
else break; else break;
} }
else { else {
if (ch != "u") parse_error("SyntaxError: Expecting UnicodeEscapeSequence -- uXXXX"); if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
ch = read_escaped_char(); ch = read_escaped_char();
if (!is_identifier_char(ch)) parse_error("SyntaxError: Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier"); if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
name += ch; name += ch;
backslash = false; backslash = false;
} }
@ -474,10 +473,10 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return name; return name;
}; };
var read_regexp = with_eof_error("SyntaxError: Unterminated regular expression", function(regexp){ var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var prev_backslash = false, ch, in_class = false; var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) { while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("SyntaxError: Unexpected line terminator"); parse_error("Unexpected line terminator");
} else if (prev_backslash) { } else if (prev_backslash) {
regexp += "\\" + ch; regexp += "\\" + ch;
prev_backslash = false; prev_backslash = false;
@ -498,7 +497,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
try { try {
return token("regexp", new RegExp(regexp, mods)); return token("regexp", new RegExp(regexp, mods));
} catch(e) { } catch(e) {
parse_error("SyntaxError: " + e.message); parse_error(e.message);
} }
}); });
@ -599,7 +598,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
} }
break; break;
} }
parse_error("SyntaxError: Unexpected character '" + ch + "'"); parse_error("Unexpected character '" + ch + "'");
}; };
next_token.context = function(nc) { next_token.context = function(nc) {
@ -756,14 +755,14 @@ function parse($TEXT, options) {
function unexpected(token) { function unexpected(token) {
if (token == null) if (token == null)
token = S.token; token = S.token;
token_error(token, "SyntaxError: Unexpected token: " + token.type + " (" + token.value + ")"); token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
}; };
function expect_token(type, val) { function expect_token(type, val) {
if (is(type, val)) { if (is(type, val)) {
return next(); return next();
} }
token_error(S.token, "SyntaxError: Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»"); token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
}; };
function expect(punc) { return expect_token("punc", punc); }; function expect(punc) { return expect_token("punc", punc); };
@ -892,7 +891,7 @@ function parse($TEXT, options) {
case "return": case "return":
if (S.in_function == 0 && !options.bare_returns) if (S.in_function == 0 && !options.bare_returns)
croak("SyntaxError: 'return' outside of function"); croak("'return' outside of function");
return new AST_Return({ return new AST_Return({
value: ( is("punc", ";") value: ( is("punc", ";")
? (next(), null) ? (next(), null)
@ -909,7 +908,7 @@ function parse($TEXT, options) {
case "throw": case "throw":
if (S.token.nlb) if (S.token.nlb)
croak("SyntaxError: Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
return new AST_Throw({ return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp) value: (tmp = expression(true), semicolon(), tmp)
}); });
@ -925,7 +924,7 @@ function parse($TEXT, options) {
case "with": case "with":
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
croak("SyntaxError: Strict mode may not include a with statement"); croak("Strict mode may not include a with statement");
} }
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesised(),
@ -945,7 +944,7 @@ function parse($TEXT, options) {
// syntactically incorrect if it contains a // syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a // LabelledStatement that is enclosed by a
// LabelledStatement with the same Identifier as label. // LabelledStatement with the same Identifier as label.
croak("SyntaxError: Label " + label.name + " defined twice"); croak("Label " + label.name + " defined twice");
} }
expect(":"); expect(":");
S.labels.push(label); S.labels.push(label);
@ -958,7 +957,7 @@ function parse($TEXT, options) {
label.references.forEach(function(ref){ label.references.forEach(function(ref){
if (ref instanceof AST_Continue) { if (ref instanceof AST_Continue) {
ref = ref.label.start; ref = ref.label.start;
croak("SyntaxError: Continue label `" + label.name + "` refers to non-IterationStatement.", croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
ref.line, ref.col, ref.pos); ref.line, ref.col, ref.pos);
} }
}); });
@ -978,11 +977,11 @@ function parse($TEXT, options) {
if (label != null) { if (label != null) {
ldef = find_if(function(l){ return l.name == label.name }, S.labels); ldef = find_if(function(l){ return l.name == label.name }, S.labels);
if (!ldef) if (!ldef)
croak("SyntaxError: Undefined label " + label.name); croak("Undefined label " + label.name);
label.thedef = ldef; label.thedef = ldef;
} }
else if (S.in_loop == 0) else if (S.in_loop == 0)
croak("SyntaxError: " + type.TYPE + " not inside a loop or switch"); croak(type.TYPE + " not inside a loop or switch");
semicolon(); semicolon();
var stat = new type({ label: label }); var stat = new type({ label: label });
if (ldef) ldef.references.push(stat); if (ldef) ldef.references.push(stat);
@ -998,7 +997,7 @@ function parse($TEXT, options) {
: expression(true, true); : expression(true, true);
if (is("operator", "in")) { if (is("operator", "in")) {
if (init instanceof AST_Var && init.definitions.length > 1) if (init instanceof AST_Var && init.definitions.length > 1)
croak("SyntaxError: Only one variable declaration allowed in for..in loop"); croak("Only one variable declaration allowed in for..in loop");
next(); next();
return for_in(init); return for_in(init);
} }
@ -1148,7 +1147,7 @@ function parse($TEXT, options) {
}); });
} }
if (!bcatch && !bfinally) if (!bcatch && !bfinally)
croak("SyntaxError: Missing catch/finally blocks"); croak("Missing catch/finally blocks");
return new AST_Try({ return new AST_Try({
body : body, body : body,
bcatch : bcatch, bcatch : bcatch,
@ -1242,7 +1241,7 @@ function parse($TEXT, options) {
break; break;
case "operator": case "operator":
if (!is_identifier_string(tok.value)) { if (!is_identifier_string(tok.value)) {
croak("SyntaxError: Invalid getter/setter name: " + tok.value, croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos); tok.line, tok.col, tok.pos);
} }
ret = _make_symbol(AST_SymbolRef); ret = _make_symbol(AST_SymbolRef);
@ -1397,7 +1396,7 @@ function parse($TEXT, options) {
function as_symbol(type, noerror) { function as_symbol(type, noerror) {
if (!is("name")) { if (!is("name")) {
if (!noerror) croak("SyntaxError: Name expected"); if (!noerror) croak("Name expected");
return null; return null;
} }
var sym = _make_symbol(type); var sym = _make_symbol(type);
@ -1461,7 +1460,7 @@ function parse($TEXT, options) {
function make_unary(ctor, op, expr) { function make_unary(ctor, op, expr) {
if ((op == "++" || op == "--") && !is_assignable(expr)) if ((op == "++" || op == "--") && !is_assignable(expr))
croak("SyntaxError: Invalid use of " + op + " operator"); croak("Invalid use of " + op + " operator");
return new ctor({ operator: op, expression: expr }); return new ctor({ operator: op, expression: expr });
}; };
@ -1525,7 +1524,7 @@ function parse($TEXT, options) {
end : prev() end : prev()
}); });
} }
croak("SyntaxError: Invalid assignment"); croak("Invalid assignment");
} }
return left; return left;
}; };

View File

@ -78,13 +78,28 @@ function repeat_string(str, i) {
return d; return d;
}; };
function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", {
get: function() {
var err = new Error(this.message);
err.name = this.name;
try {
throw err;
} catch(e) {
return e.stack;
}
}
});
}
function DefaultsError(msg, defs) { function DefaultsError(msg, defs) {
Error.call(this, msg); this.message = msg;
this.msg = msg;
this.defs = defs; this.defs = defs;
}; };
DefaultsError.prototype = Object.create(Error.prototype); DefaultsError.prototype = Object.create(Error.prototype);
DefaultsError.prototype.constructor = DefaultsError; DefaultsError.prototype.constructor = DefaultsError;
DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError);
DefaultsError.croak = function(msg, defs) { DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs); throw new DefaultsError(msg, defs);

View File

@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "2.7.5", "version": "2.8.0",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@ -50,7 +50,8 @@ ifs_3_should_warn: {
conditionals : true, conditionals : true,
dead_code : true, dead_code : true,
evaluate : true, evaluate : true,
booleans : true booleans : true,
side_effects : true,
}; };
input: { input: {
var x, y; var x, y;
@ -135,16 +136,28 @@ ifs_6: {
comparisons: true comparisons: true
}; };
input: { input: {
var x; var x, y;
if (!foo && !bar && !baz && !boo) { if (!foo && !bar && !baz && !boo) {
x = 10; x = 10;
} else { } else {
x = 20; x = 20;
} }
if (y) {
x[foo] = 10;
} else {
x[foo] = 20;
}
if (foo) {
x[bar] = 10;
} else {
x[bar] = 20;
}
} }
expect: { expect: {
var x; var x, y;
x = foo || bar || baz || boo ? 20 : 10; x = foo || bar || baz || boo ? 20 : 10;
x[foo] = y ? 10 : 20;
foo ? x[bar] = 10 : x[bar] = 20;
} }
} }
@ -159,10 +172,16 @@ cond_1: {
} else { } else {
do_something(y); do_something(y);
} }
if (some_condition()) {
side_effects(x);
} else {
side_effects(y);
}
} }
expect: { expect: {
var do_something; var do_something;
do_something(some_condition() ? x : y); do_something(some_condition() ? x : y);
some_condition() ? side_effects(x) : side_effects(y);
} }
} }
@ -213,10 +232,16 @@ cond_4: {
} else { } else {
do_something(); do_something();
} }
if (some_condition()) {
side_effects();
} else {
side_effects();
}
} }
expect: { expect: {
var do_something; var do_something;
some_condition(), do_something(); some_condition(), do_something();
some_condition(), side_effects();
} }
} }
@ -250,7 +275,8 @@ cond_5: {
cond_7: { cond_7: {
options = { options = {
conditionals: true, conditionals: true,
evaluate : true evaluate : true,
side_effects: true,
}; };
input: { input: {
var x, y, z, a, b; var x, y, z, a, b;
@ -714,6 +740,7 @@ issue_1154: {
conditionals: true, conditionals: true,
evaluate : true, evaluate : true,
booleans : true, booleans : true,
side_effects: true,
}; };
input: { input: {
function f1(x) { return x ? -1 : -1; } function f1(x) { return x ? -1 : -1; }
@ -742,7 +769,7 @@ issue_1154: {
function g2() { return g(), 2; } function g2() { return g(), 2; }
function g3() { return g(), -4; } function g3() { return g(), -4; }
function g4() { return g(), !1; } function g4() { return g(), !1; }
function g5() { return g(), void 0; } function g5() { return void g(); }
function g6() { return g(), "number"; } function g6() { return g(), "number"; }
} }
} }
@ -750,7 +777,8 @@ issue_1154: {
no_evaluate: { no_evaluate: {
options = { options = {
conditionals: true, conditionals: true,
evaluate : false evaluate : false,
side_effects: true,
} }
input: { input: {
function f(b) { function f(b) {

View File

@ -64,7 +64,8 @@ dead_code_constant_boolean_should_warn_more: {
loops : true, loops : true,
booleans : true, booleans : true,
conditionals : true, conditionals : true,
evaluate : true evaluate : true,
side_effects : true,
}; };
input: { input: {
while (!((foo && bar) || (x + "0"))) { while (!((foo && bar) || (x + "0"))) {

View File

@ -624,25 +624,35 @@ in_boolean_context: {
options = { options = {
booleans: true, booleans: true,
evaluate: true, evaluate: true,
sequences: true,
side_effects: true,
} }
input: { input: {
!42; console.log(
!"foo"; !42,
![1, 2]; !"foo",
!/foo/; ![1, 2],
!b(42); !/foo/,
!b("foo"); !b(42),
!b([1, 2]); !b("foo"),
!b(/foo/); !b([1, 2]),
!b(/foo/),
![1, foo()],
![1, foo(), 2]
);
} }
expect: { expect: {
!1; console.log(
!1; !1,
!1; !1,
!1; !1,
!b(42); !1,
!b("foo"); !b(42),
!b([1, 2]); !b("foo"),
!b(/foo/); !b([1, 2]),
!b(/foo/),
![1, foo()],
(foo(), !1)
);
} }
} }

View File

@ -116,3 +116,61 @@ pure_function_calls_toplevel: {
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]", "WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
] ]
} }
should_warn: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
side_effects: true,
}
input: {
/* @__PURE__ */(function(){x})(), void/* @__PURE__ */(function(){y})();
/* @__PURE__ */(function(){x})() || true ? foo() : bar();
true || /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() && false ? foo() : bar();
false && /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() + "foo" ? bar() : baz();
"foo" + /* @__PURE__ */(function(){y})() ? bar() : baz();
/* @__PURE__ */(function(){x})() ? foo() : foo();
[/* @__PURE__ */(function(){x})()] ? foo() : bar();
!{ foo: /* @__PURE__ */(function(){x})() } ? bar() : baz();
}
expect: {
foo();
foo();
bar();
bar();
bar();
bar();
foo();
foo();
baz();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:128,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:129,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]",
"WARN: Condition always true [test/compress/issue-1261.js:129,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:130,8]",
"WARN: Condition always true [test/compress/issue-1261.js:130,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:131,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:131,23]",
"WARN: Condition always false [test/compress/issue-1261.js:131,23]",
"WARN: Boolean && always false [test/compress/issue-1261.js:132,8]",
"WARN: Condition always false [test/compress/issue-1261.js:132,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:133,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:133,23]",
"WARN: Condition always true [test/compress/issue-1261.js:133,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:134,31]",
"WARN: Condition always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:135,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:136,24]",
"WARN: Condition always true [test/compress/issue-1261.js:136,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,31]",
"WARN: Condition always false [test/compress/issue-1261.js:137,8]",
]
}

View File

@ -35,7 +35,7 @@ string_plus_optimization: {
throw "nope"; throw "nope";
} }
try { try {
console.log('0' + throwing_function() ? "yes" : "no"); console.log((throwing_function(), "yes"));
} catch (ex) { } catch (ex) {
console.log(ex); console.log(ex);
} }

View File

@ -29,6 +29,7 @@ typeof_in_boolean_context: {
booleans : true, booleans : true,
evaluate : true, evaluate : true,
conditionals : true, conditionals : true,
side_effects : true,
}; };
input: { input: {
function f1(x) { return typeof x ? "yes" : "no"; } function f1(x) { return typeof x ? "yes" : "no"; }
@ -36,12 +37,14 @@ typeof_in_boolean_context: {
typeof 0 ? foo() : bar(); typeof 0 ? foo() : bar();
!typeof console.log(1); !typeof console.log(1);
var a = !typeof console.log(2); var a = !typeof console.log(2);
if (typeof (1 + foo()));
} }
expect: { expect: {
function f1(x) { return "yes"; } function f1(x) { return "yes"; }
function f2() { return g(), "Yes"; } function f2() { return g(), "Yes"; }
foo(); foo();
!(console.log(1), !0); console.log(1);
var a = !(console.log(2), !0); var a = !(console.log(2), !0);
foo();
} }
} }

View File

@ -0,0 +1 @@
foo, bar(

View File

@ -0,0 +1 @@
function f(a{}

View File

@ -0,0 +1 @@
foo( xyz, 0abc);

View File

@ -199,4 +199,43 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should fail with invalid syntax", function(done) {
var command = uglifyjscmd + ' test/input/invalid/simple.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/simple.js:1,12");
assert.strictEqual(lines[1], "function f(a{}");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token punc «{», expected punc «,»");
done();
});
});
it("Should fail with correct marking of tabs", function(done) {
var command = uglifyjscmd + ' test/input/invalid/tab.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/tab.js:1,12");
assert.strictEqual(lines[1], "\t\tfoo(\txyz, 0abc);");
assert.strictEqual(lines[2], "\t\t \t ^");
assert.strictEqual(lines[3], "SyntaxError: Invalid syntax: 0abc");
done();
});
});
it("Should fail with correct marking at start of line", function(done) {
var command = uglifyjscmd + ' test/input/invalid/eof.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
var lines = stderr.split(/\n/);
assert.strictEqual(lines[0], "Parse error at test/input/invalid/eof.js:2,0");
assert.strictEqual(lines[1], "foo, bar(");
assert.strictEqual(lines[2], " ^");
assert.strictEqual(lines[3], "SyntaxError: Unexpected token: eof (undefined)");
done();
});
});
}); });

View File

@ -13,7 +13,7 @@ describe("Comment", function() {
var fail = function(e) { var fail = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: operator (>)" && e.message === "Unexpected token: operator (>)" &&
e.line === 2 && e.line === 2 &&
e.col === 0; e.col === 0;
} }
@ -36,7 +36,7 @@ describe("Comment", function() {
var fail = function(e) { var fail = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected token: operator (>)" && e.message === "Unexpected token: operator (>)" &&
e.line === 5 && e.line === 5 &&
e.col === 0; e.col === 0;
} }

View File

@ -168,7 +168,7 @@ describe("Directives", function() {
throw new Error("Expected parser to fail"); throw new Error("Expected parser to fail");
} catch (e) { } catch (e) {
assert.strictEqual(e instanceof uglify.JS_Parse_Error, true); assert.strictEqual(e instanceof uglify.JS_Parse_Error, true);
assert.strictEqual(e.message, "SyntaxError: Unexpected token: punc (])"); assert.strictEqual(e.message, "Unexpected token: punc (])");
} }
test_directive(tokenizer, tests[i]); test_directive(tokenizer, tests[i]);

View File

@ -71,7 +71,7 @@ describe("Getters and setters", function() {
var fail = function(data) { var fail = function(data) {
return function (e) { return function (e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Invalid getter/setter name: " + data.operator; e.message === "Invalid getter/setter name: " + data.operator;
}; };
}; };

View File

@ -50,7 +50,7 @@ describe("line-endings", function() {
} }
var fail = function(e) { var fail = function(e) {
return e instanceof Uglify.JS_Parse_Error && return e instanceof Uglify.JS_Parse_Error &&
e.message === "SyntaxError: Unexpected line terminator"; e.message === "Unexpected line terminator";
} }
for (var i = 0; i < inputs.length; i++) { for (var i = 0; i < inputs.length; i++) {
assert.throws(test(inputs[i]), fail); assert.throws(test(inputs[i]), fail);

View File

@ -110,7 +110,7 @@ describe("minify", function() {
inSourceMap: "inline", inSourceMap: "inline",
sourceMapInline: true sourceMapInline: true
}); });
}, "multiple input and inline source map"); });
}); });
it("Should fail with SpiderMonkey and inline source map", function() { it("Should fail with SpiderMonkey and inline source map", function() {
assert.throws(function() { assert.throws(function() {
@ -119,7 +119,7 @@ describe("minify", function() {
sourceMapInline: true, sourceMapInline: true,
spidermonkey: true spidermonkey: true
}); });
}, "SpiderMonkey and inline source map"); });
}); });
}); });
@ -154,6 +154,32 @@ describe("minify", function() {
var code = result.code; var code = result.code;
assert.strictEqual(code, "// comment1 comment2\nbar();"); assert.strictEqual(code, "// comment1 comment2\nbar();");
}); });
it("should not drop #__PURE__ hint if function is retained", function() {
var result = Uglify.minify("var a = /*#__PURE__*/(function(){return 1})();", {
fromString: true,
output: {
comments: "all",
beautify: false,
}
});
var code = result.code;
assert.strictEqual(code, "var a=/*#__PURE__*/function(){return 1}();");
})
});
describe("JS_Parse_Error", function() {
it("should throw syntax error", function() {
assert.throws(function() {
Uglify.minify("function f(a{}", { fromString: true });
}, function(err) {
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token punc «{», expected punc «,»");
assert.strictEqual(err.filename, 0);
assert.strictEqual(err.line, 1);
assert.strictEqual(err.col, 12);
return true;
});
});
}); });
}); });

View File

@ -15,7 +15,7 @@ describe("Number literals", function () {
} }
var error = function(e) { var error = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Legacy octal literals are not allowed in strict mode"; e.message === "Legacy octal literals are not allowed in strict mode";
} }
for (var i = 0; i < inputs.length; i++) { for (var i = 0; i < inputs.length; i++) {
assert.throws(test(inputs[i]), error, inputs[i]); assert.throws(test(inputs[i]), error, inputs[i]);

View File

@ -19,7 +19,7 @@ describe("String literals", function() {
var error = function(e) { var error = function(e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Unterminated string constant"; e.message === "Unterminated string constant";
}; };
for (var input in inputs) { for (var input in inputs) {
@ -49,7 +49,7 @@ describe("String literals", function() {
var error = function(e) { var error = function(e) {
return e instanceof UglifyJS.JS_Parse_Error && return e instanceof UglifyJS.JS_Parse_Error &&
e.message === "SyntaxError: Legacy octal escape sequences are not allowed in strict mode"; e.message === "Legacy octal escape sequences are not allowed in strict mode";
} }
for (var input in inputs) { for (var input in inputs) {

View File

@ -9,7 +9,7 @@ describe("With", function() {
} }
var error = function(e) { var error = function(e) {
return e instanceof uglify.JS_Parse_Error && return e instanceof uglify.JS_Parse_Error &&
e.message === "SyntaxError: Strict mode may not include a with statement"; e.message === "Strict mode may not include a with statement";
} }
assert.throws(test, error); assert.throws(test, error);
}); });