Compare commits

..

7 Commits

Author SHA1 Message Date
Alex Lam S.L
75e2748b16 v2.8.27 2017-05-19 17:44:50 +08:00
Alex Lam S.L
a7c987ad2b Merge pull request #1971 from alexlamsl/v2.8.27 2017-05-19 17:21:39 +08:00
Alex Lam S.L
957c54bc87 introduce unsafe_regexp (#1970)
fixes #1964
2017-05-19 10:10:09 +08:00
Alex Lam S.L
4cbf5a7821 v2.8.26 2017-05-16 07:13:58 +08:00
Alex Lam S.L
93d4224072 Merge pull request #1947 from alexlamsl/v2.8.26 2017-05-16 07:12:28 +08:00
Alex Lam S.L
6cd580dc23 fix parsing of property access after new line (#1944)
Account for comments when detecting property access in `tokenizer`.

fixes #1943
2017-05-16 06:34:10 +08:00
Alex Lam S.L
ecb63ad8bc improve keyword-related parser errors (#1941)
fixes #1937
2017-05-16 06:33:57 +08:00
10 changed files with 326 additions and 182 deletions

View File

@@ -1,5 +1,4 @@
language: node_js language: node_js
before_install: "npm install -g npm"
node_js: node_js:
- "0.10" - "0.10"
- "0.12" - "0.12"

View File

@@ -353,6 +353,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `unsafe_proto` (default: false) -- optimize expressions like - `unsafe_proto` (default: false) -- optimize expressions like
`Array.prototype.slice.call(a)` into `[].slice.call(a)` `Array.prototype.slice.call(a)` into `[].slice.call(a)`
- `unsafe_regexp` (default: false) -- enable substitutions of variables with
`RegExp` values the same way as if they are constants.
- `conditionals` -- apply optimizations for `if`-s and conditional - `conditionals` -- apply optimizations for `if`-s and conditional
expressions expressions
@@ -443,10 +446,10 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from - `keep_infinity` -- default `false`. Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome. being compressed into `1/0`, which may cause performance issues on Chrome.
- `side_effects` -- default `false`. Pass `true` to potentially drop functions - `side_effects` -- default `true`. Pass `false` to disable potentially dropping
marked as "pure". A function call is marked as "pure" if a comment annotation functions marked as "pure". A function call is marked as "pure" if a comment
`/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For example: annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
`/*@__PURE__*/foo()`; example: `/*@__PURE__*/foo();`
### The `unsafe` option ### The `unsafe` option

View File

@@ -84,6 +84,7 @@ function Compressor(options, false_by_default) {
unsafe_comps : false, unsafe_comps : false,
unsafe_math : false, unsafe_math : false,
unsafe_proto : false, unsafe_proto : false,
unsafe_regexp : false,
unused : !false_by_default, unused : !false_by_default,
warnings : true, warnings : true,
}, true); }, true);
@@ -3680,7 +3681,7 @@ merge(Compressor.prototype, {
if (fixed) { if (fixed) {
if (d.should_replace === undefined) { if (d.should_replace === undefined) {
var init = fixed.evaluate(compressor); var init = fixed.evaluate(compressor);
if (init !== fixed) { if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) {
init = make_node_from_constant(init, fixed); init = make_node_from_constant(init, fixed);
var value = init.optimize(compressor).print_to_string().length; var value = init.optimize(compressor).print_to_string().length;
var fn; var fn;

View File

@@ -285,7 +285,11 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) ||
(type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) || (type == "keyword" && KEYWORDS_BEFORE_EXPRESSION(value)) ||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value))); (type == "punc" && PUNC_BEFORE_EXPRESSION(value)));
prev_was_dot = (type == "punc" && value == "."); if (type == "punc" && value == ".") {
prev_was_dot = true;
} else if (!is_comment) {
prev_was_dot = false;
}
var ret = { var ret = {
type : type, type : type,
value : value, value : value,
@@ -803,17 +807,16 @@ function parse($TEXT, options) {
}; };
var statement = embed_tokens(function() { var statement = embed_tokens(function() {
var tmp;
handle_regexp(); handle_regexp();
switch (S.token.type) { switch (S.token.type) {
case "string": case "string":
if (S.in_directives) { if (S.in_directives) {
tmp = peek(); var token = peek();
if (S.token.raw.indexOf("\\") == -1 if (S.token.raw.indexOf("\\") == -1
&& (tmp.nlb && (token.nlb
|| is_token(tmp, "eof") || is_token(token, "eof")
|| is_token(tmp, "punc", ";") || is_token(token, "punc", ";")
|| is_token(tmp, "punc", "}"))) { || is_token(token, "punc", "}"))) {
S.input.add_directive(S.token.value); S.input.add_directive(S.token.value);
} else { } else {
S.in_directives = false; S.in_directives = false;
@@ -852,75 +855,103 @@ function parse($TEXT, options) {
} }
case "keyword": case "keyword":
switch (tmp = S.token.value, next(), tmp) { switch (S.token.value) {
case "break": case "break":
next();
return break_cont(AST_Break); return break_cont(AST_Break);
case "continue": case "continue":
next();
return break_cont(AST_Continue); return break_cont(AST_Continue);
case "debugger": case "debugger":
next();
semicolon(); semicolon();
return new AST_Debugger(); return new AST_Debugger();
case "do": case "do":
next();
var body = in_loop(statement);
expect_token("keyword", "while");
var condition = parenthesised();
semicolon(true);
return new AST_Do({ return new AST_Do({
body : in_loop(statement), body : body,
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp) condition : condition
}); });
case "while": case "while":
next();
return new AST_While({ return new AST_While({
condition : parenthesised(), condition : parenthesised(),
body : in_loop(statement) body : in_loop(statement)
}); });
case "for": case "for":
next();
return for_(); return for_();
case "function": case "function":
next();
return function_(AST_Defun); return function_(AST_Defun);
case "if": case "if":
next();
return if_(); return if_();
case "return": case "return":
if (S.in_function == 0 && !options.bare_returns) if (S.in_function == 0 && !options.bare_returns)
croak("'return' outside of function"); croak("'return' outside of function");
next();
var value = null;
if (is("punc", ";")) {
next();
} else if (!can_insert_semicolon()) {
value = expression(true);
semicolon();
}
return new AST_Return({ return new AST_Return({
value: ( is("punc", ";") value: value
? (next(), null)
: can_insert_semicolon()
? null
: (tmp = expression(true), semicolon(), tmp) )
}); });
case "switch": case "switch":
next();
return new AST_Switch({ return new AST_Switch({
expression : parenthesised(), expression : parenthesised(),
body : in_loop(switch_body_) body : in_loop(switch_body_)
}); });
case "throw": case "throw":
next();
if (S.token.nlb) if (S.token.nlb)
croak("Illegal newline after 'throw'"); croak("Illegal newline after 'throw'");
var value = expression(true);
semicolon();
return new AST_Throw({ return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp) value: value
}); });
case "try": case "try":
next();
return try_(); return try_();
case "var": case "var":
return tmp = var_(), semicolon(), tmp; next();
var node = var_();
semicolon();
return node;
case "const": case "const":
return tmp = const_(), semicolon(), tmp; next();
var node = const_();
semicolon();
return node;
case "with": case "with":
if (S.input.has_directive("use strict")) { if (S.input.has_directive("use strict")) {
croak("Strict mode may not include a with statement"); croak("Strict mode may not include a with statement");
} }
next();
return new AST_With({ return new AST_With({
expression : parenthesised(), expression : parenthesised(),
body : statement() body : statement()

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.8.25", "version": "2.8.27",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -989,3 +989,50 @@ Infinity_NaN_undefined_LHS: {
"}", "}",
] ]
} }
issue_1964_1: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: false,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect_stdout: "b"
}
issue_1964_2: {
options = {
evaluate: true,
reduce_vars: true,
unsafe_regexp: true,
unused: true,
}
input: {
function f() {
var long_variable_name = /\s/;
return "a b c".split(long_variable_name)[1];
}
console.log(f());
}
expect: {
function f() {
return "a b c".split(/\s/)[1];
}
console.log(f());
}
expect_stdout: "b"
}

View File

@@ -0,0 +1,31 @@
operator: {
input: {
a. //comment
typeof
}
expect_exact: "a.typeof;"
}
name: {
input: {
a. //comment
b
}
expect_exact: "a.b;"
}
keyword: {
input: {
a. //comment
default
}
expect_exact: "a.default;"
}
atom: {
input: {
a. //comment
true
}
expect_exact: "a.true;"
}

View File

@@ -0,0 +1 @@
if (0) else 1;

View File

@@ -0,0 +1 @@
return 42;

View File

@@ -381,4 +381,34 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should throw syntax error (else)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/else.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/else.js:1,7",
"if (0) else 1;",
" ^",
"SyntaxError: Unexpected token: keyword (else)"
].join("\n"));
done();
});
});
it("Should throw syntax error (return)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/return.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/return.js:1,0",
"return 42;",
"^",
"SyntaxError: 'return' outside of function"
].join("\n"));
done();
});
});
}); });