diff --git a/lib/compress.js b/lib/compress.js index 9d6eb098..b07ac261 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -346,7 +346,7 @@ merge(Compressor.prototype, { if (ref.scope.uses_eval || ref.scope.uses_with) break; // Constant single use vars can be replaced in any scope. - if (var_decl.value.is_constant(compressor)) { + if (!(var_decl.value instanceof AST_RegExp) && var_decl.value.is_constant(compressor)) { var ctt = new TreeTransformer(function(node) { if (node === ref) return replace_var(node, ctt.parent(), true); diff --git a/lib/output.js b/lib/output.js index 5de1596d..a26bd06b 100644 --- a/lib/output.js +++ b/lib/output.js @@ -520,7 +520,8 @@ function OutputStream(options) { PARENS([ AST_Unary, AST_Undefined ], function(output){ var p = output.parent(); - return p instanceof AST_PropAccess && p.expression === this; + return p instanceof AST_PropAccess && p.expression === this + || p instanceof AST_New; }); PARENS(AST_Seq, function(output){ @@ -1531,7 +1532,12 @@ function OutputStream(options) { // self should be AST_New. decide if we want to show parens or not. function no_constructor_parens(self, output) { - return self.args.length == 0 && !output.option("beautify"); + return self.args.length == 0 && !output.option("beautify") || + !(self.expression instanceof AST_SymbolRef || + self.expression instanceof AST_Call || + self.expression instanceof AST_Function || + self.expression instanceof AST_Assign + ); }; function best_of(a) { diff --git a/lib/parse.js b/lib/parse.js index b776ec12..14da6b01 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -501,6 +501,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) { break; } else if (ch == "\\") { prev_backslash = true; + } else if ("\r\n\u2028\u2029".indexOf(ch) >= 0) { + parse_error("Unexpected line terminator"); } else { regexp += ch; } diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 934a5c73..ef7af9ed 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -1153,3 +1153,59 @@ collapse_vars_short_circuited_conditions: { } } +collapse_vars_regexp: { + options = { + collapse_vars: true, + loops: false, + sequences: true, + dead_code: true, + conditionals: true, + comparisons: true, + evaluate: true, + booleans: true, + unused: true, + hoist_funs: true, + keep_fargs: true, + if_return: true, + join_vars: true, + cascade: true, + side_effects: true, + } + input: { + function f1() { + var k = 9; + var rx = /[A-Z]+/; + return [rx, k]; + } + function f2() { + var rx = /[abc123]+/; + return function(s) { + return rx.exec(s); + }; + } + (function(){ + var result; + var s = 'acdabcdeabbb'; + var rx = /ab*/g; + while (result = rx.exec(s)) { + console.log(result[0]); + } + })(); + } + expect: { + function f1() { + return [/[A-Z]+/, 9]; + } + function f2() { + var rx = /[abc123]+/; + return function(s) { + return rx.exec(s); + }; + } + (function(){ + var result, rx = /ab*/g; + while (result = rx.exec('acdabcdeabbb')) + console.log(result[0]); + })(); + } +} diff --git a/test/compress/new.js b/test/compress/new.js index 4b2c51c3..d956ae27 100644 --- a/test/compress/new.js +++ b/test/compress/new.js @@ -10,3 +10,11 @@ new_statement: { } expect_exact: "new x(1);new x(1)(2);new x(1)(2)(3);new new x(1);new new x(1)(2);new new x(1)(2);(new new x(1))(2);" } + +new_with_rewritten_true_value: { + options = { booleans: true } + input: { + new true; + } + expect_exact: "new(!0);" +} diff --git a/test/mocha/line-endings.js b/test/mocha/line-endings.js index 8fd56a19..3457dd70 100644 --- a/test/mocha/line-endings.js +++ b/test/mocha/line-endings.js @@ -30,5 +30,27 @@ describe("line-endings", function() { var result = Uglify.minify(js, options); assert.strictEqual(result.code, expected_code); }); + + it("Should not allow line terminators in regexp", function() { + var inputs = [ + "/\n/", + "/\r/", + "/\u2028/", + "/\u2029/", + "/someRandomTextLike[]()*AndThen\n/" + ] + var test = function(input) { + return function() { + Uglify.parse(input); + } + } + var fail = function(e) { + return e instanceof Uglify.JS_Parse_Error && + e.message === "Unexpected line terminator"; + } + for (var i = 0; i < inputs.length; i++) { + assert.throws(test(inputs[i]), fail); + } + }); }); diff --git a/test/mocha/new.js b/test/mocha/new.js new file mode 100644 index 00000000..7e7aea7d --- /dev/null +++ b/test/mocha/new.js @@ -0,0 +1,34 @@ +var assert = require("assert"); +var uglify = require("../../"); + +describe("New", function() { + it("Should attach callback parens after some tokens", function() { + var tests = [ + "new x(1);", + "new (function(foo){this.foo=foo;})(1);", + "new true;", + "new (0);", + "new (!0);", + "new (bar = function(foo) {this.foo=foo;})(123);" + ]; + var expected = [ + "new x(1);", + "new function(foo) {\n this.foo = foo;\n}(1);", + "new true;", + "new 0;", + "new (!0);", + "new (bar = function(foo) {\n this.foo = foo;\n})(123);" + ]; + for (var i = 0; i < tests.length; i++) { + assert.strictEqual( + uglify.minify(tests[i], { + fromString: true, + output: {beautify: true}, + compress: false, + mangle: false + }).code, + expected[i] + ); + } + }); +}); \ No newline at end of file diff --git a/test/run-tests.js b/test/run-tests.js index b6677475..1c98a1ae 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -163,9 +163,16 @@ function run_compress_tests() { function parse_test(file) { var script = fs.readFileSync(file, "utf8"); - var ast = U.parse(script, { - filename: file - }); + // TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348 + try { + var ast = U.parse(script, { + filename: file + }); + } catch (e) { + console.log("Caught error while parsing tests in " + file + "\n"); + console.log(e); + throw e; + } var tests = {}; var tw = new U.TreeWalker(function(node, descend){ if (node instanceof U.AST_LabeledStatement diff --git a/tools/exports.js b/tools/exports.js index 110b5c4e..09acc13e 100644 --- a/tools/exports.js +++ b/tools/exports.js @@ -14,5 +14,6 @@ exports["merge"] = merge; exports["parse"] = parse; exports["push_uniq"] = push_uniq; exports["string_template"] = string_template; +exports["tokenizer"] = tokenizer; exports["is_identifier"] = is_identifier; exports["SymbolDef"] = SymbolDef;