diff --git a/lib/ast.js b/lib/ast.js index 2972b7aacbd..f4e89ab95d9 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -495,10 +495,10 @@ var AST_Finally = DEFNODE("Finally", null, { $documentation: "A `finally` node; only makes sense as part of a `try` statement" }, AST_Block); -/* -----[ VAR ]----- */ +/* -----[ VAR/CONST ]----- */ var AST_Definitions = DEFNODE("Definitions", "definitions", { - $documentation: "Base class for `var` nodes (variable declarations/initializations)", + $documentation: "Base class for `var` or `const` nodes (variable declarations/initializations)", $propdoc: { definitions: "[AST_VarDef*] array of variable definitions" }, @@ -516,10 +516,14 @@ var AST_Var = DEFNODE("Var", null, { $documentation: "A `var` statement" }, AST_Definitions); +var AST_Const = DEFNODE("Const", null, { + $documentation: "A `const` statement" +}, AST_Definitions); + var AST_VarDef = DEFNODE("VarDef", "name value", { $documentation: "A variable declaration; only appears in a AST_Definitions node", $propdoc: { - name: "[AST_SymbolVar] name of the variable", + name: "[AST_SymbolVar|AST_SymbolConst] name of the variable", value: "[AST_Node?] initializer, or null of there's no initializer" }, _walk: function(visitor) { @@ -724,13 +728,17 @@ var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { }, AST_Symbol); var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { - $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)", + $documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)", }, AST_Symbol); var AST_SymbolVar = DEFNODE("SymbolVar", null, { $documentation: "Symbol defining a variable", }, AST_SymbolDeclaration); +var AST_SymbolConst = DEFNODE("SymbolConst", null, { + $documentation: "A constant declaration" +}, AST_SymbolDeclaration); + var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { $documentation: "Symbol naming a function argument", }, AST_SymbolVar); diff --git a/lib/compress.js b/lib/compress.js index f168b94218a..7f3313f1a19 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -486,7 +486,8 @@ merge(Compressor.prototype, { if (def.fixed === false) return false; if (def.fixed != null && (!value || def.references.length > 0)) return false; return !def.orig.some(function(sym) { - return sym instanceof AST_SymbolDefun + return sym instanceof AST_SymbolConst + || sym instanceof AST_SymbolDefun || sym instanceof AST_SymbolLambda; }); } @@ -503,7 +504,7 @@ merge(Compressor.prototype, { def.escaped = false; if (def.scope.uses_eval) { def.fixed = false; - } else if (!def.global || compressor.toplevel(def)) { + } else if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) { def.fixed = undefined; } else { def.fixed = false; @@ -537,6 +538,14 @@ merge(Compressor.prototype, { return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda; } + function is_reference_const(ref) { + if (!(ref instanceof AST_SymbolRef)) return false; + var orig = ref.definition().orig; + for (var i = orig.length; --i >= 0;) { + if (orig[i] instanceof AST_SymbolConst) return true; + } + } + function find_variable(compressor, name) { var scope, i = 0; while (scope = compressor.parent(i++)) { @@ -802,7 +811,8 @@ merge(Compressor.prototype, { return make_node(AST_SymbolRef, expr.name, expr.name); } } else { - return expr[expr instanceof AST_Assign ? "left" : "expression"]; + var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"]; + return !is_reference_const(lhs) && lhs; } } @@ -1990,6 +2000,7 @@ merge(Compressor.prototype, { && node instanceof AST_Assign && node.operator == "=" && node.left instanceof AST_SymbolRef + && !is_reference_const(node.left) && scope === self) { node.right.walk(tw); return true; @@ -3196,7 +3207,7 @@ merge(Compressor.prototype, { && (left.operator == "++" || left.operator == "--")) { left = left.expression; } else left = null; - if (!left || is_lhs_read_only(left)) { + if (!left || is_lhs_read_only(left) || is_reference_const(left)) { expressions[++i] = cdr; continue; } diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 8d7ee4b8520..2a1da9e6869 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -168,7 +168,7 @@ }); }, VariableDeclaration: function(M) { - return new AST_Var({ + return new (M.kind === "const" ? AST_Const : AST_Var)({ start : my_start_token(M), end : my_end_token(M), definitions : M.declarations.map(from_moz) @@ -204,7 +204,7 @@ Identifier: function(M) { var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2]; return new ( p.type == "LabeledStatement" ? AST_Label - : p.type == "VariableDeclarator" && p.id === M ? AST_SymbolVar + : p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar) : p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg) : p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg) : p.type == "CatchClause" ? AST_SymbolCatch @@ -324,7 +324,7 @@ def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) { return { type: "VariableDeclaration", - kind: "var", + kind: M instanceof AST_Const ? "const" : "var", declarations: M.definitions.map(to_moz) }; }); diff --git a/lib/output.js b/lib/output.js index 0b98825fbc5..33f4c5330d8 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1033,6 +1033,9 @@ function OutputStream(options) { DEFPRINT(AST_Var, function(self, output){ self._do_print(output, "var"); }); + DEFPRINT(AST_Const, function(self, output){ + self._do_print(output, "const"); + }); function parenthesize_for_noin(node, output, noin) { if (!noin) node.print(output); diff --git a/lib/parse.js b/lib/parse.js index 74c00b748a9..92690dfb5a6 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -912,6 +912,9 @@ function parse($TEXT, options) { case "var": return tmp = var_(), semicolon(), tmp; + case "const": + return tmp = const_(), semicolon(), tmp; + case "with": if (S.input.has_directive("use strict")) { croak("Strict mode may not include a with statement"); @@ -1146,13 +1149,16 @@ function parse($TEXT, options) { }); }; - function vardefs(no_in) { + function vardefs(no_in, in_const) { var a = []; for (;;) { a.push(new AST_VarDef({ start : S.token, - name : as_symbol(AST_SymbolVar), - value : is("operator", "=") ? (next(), expression(false, no_in)) : null, + name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), + value : is("operator", "=") + ? (next(), expression(false, no_in)) + : in_const && S.input.has_directive("use strict") + ? croak("Missing initializer in const declaration") : null, end : prev() })); if (!is("punc", ",")) @@ -1165,7 +1171,15 @@ function parse($TEXT, options) { var var_ = function(no_in) { return new AST_Var({ start : prev(), - definitions : vardefs(no_in), + definitions : vardefs(no_in, false), + end : prev() + }); + }; + + var const_ = function() { + return new AST_Const({ + start : prev(), + definitions : vardefs(false, true), end : prev() }); }; diff --git a/lib/scope.js b/lib/scope.js index 14ffb46fba0..65144be3713 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -156,7 +156,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ // later. (node.scope = defun.parent_scope).def_function(node); } - else if (node instanceof AST_SymbolVar) { + else if (node instanceof AST_SymbolVar + || node instanceof AST_SymbolConst) { defun.def_variable(node); if (defun !== scope) { node.mark_enclosed(options); @@ -267,7 +268,7 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){ AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Scope.prototype.init_scope_vars.apply(this, arguments); this.uses_arguments = false; - this.def_variable(new AST_SymbolFunarg({ + this.def_variable(new AST_SymbolConst({ name: "arguments", start: this.start, end: this.end @@ -485,6 +486,8 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){ } else if (node instanceof AST_Var) base54.consider("var"); + else if (node instanceof AST_Const) + base54.consider("const"); else if (node instanceof AST_Lambda) base54.consider("function"); else if (node instanceof AST_For) diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 90d7ac93adb..0d578d7de09 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -583,8 +583,8 @@ collapse_vars_assignment: { return a = a; } function f1(c) { - var a = 3 / c; - var b = 1 - a; + const a = 3 / c; + const b = 1 - a; return b; } function f2(c) { @@ -724,10 +724,10 @@ collapse_vars_misc1: { return t; } function f1(x) { var y = 5 - x; return y; } - function f2(x) { var z = foo(), y = z / (5 - x); return y; } + function f2(x) { const z = foo(), y = z / (5 - x); return y; } function f3(x) { var z = foo(), y = (5 - x) / z; return y; } function f4(x) { var z = foo(), y = (5 - u) / z; return y; } - function f5(x) { var z = foo(), y = (5 - window.x) / z; return y; } + function f5(x) { const z = foo(), y = (5 - window.x) / z; return y; } function f6() { var b = window.a * window.z; return b && zap(); } function f7() { var b = window.a * window.z; return b + b; } function f8() { var b = window.a * window.z; var c = b + 5; return b + c; } @@ -744,7 +744,7 @@ collapse_vars_misc1: { function f2(x) { return foo() / (5 - x) } function f3(x) { return (5 - x) / foo() } function f4(x) { var z = foo(); return (5 - u) / z } - function f5(x) { var z = foo(); return (5 - window.x) / z } + function f5(x) { const z = foo(); return (5 - window.x) / z } function f6() { return window.a * window.z && zap() } function f7() { var b = window.a * window.z; return b + b } function f8() { var b = window.a * window.z; return b + (b + 5) } @@ -2186,3 +2186,49 @@ compound_assignment: { } expect_stdout: "4" } + +reassign_const_1: { + options = { + collapse_vars: true, + } + input: { + function f() { + const a = 1; + a = 2; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + a = 2; + return a; + } + console.log(f()); + } + expect_stdout: true +} + +reassign_const_2: { + options = { + collapse_vars: true, + } + input: { + function f() { + const a = 1; + ++a; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + ++a; + return a; + } + console.log(f()); + } + expect_stdout: true +} diff --git a/test/compress/const.js b/test/compress/const.js new file mode 100644 index 00000000000..a88d5946717 --- /dev/null +++ b/test/compress/const.js @@ -0,0 +1,166 @@ +issue_1191: { + options = { + evaluate : true, + booleans : true, + comparisons : true, + dead_code : true, + conditionals : true, + side_effects : true, + unused : true, + hoist_funs : true, + if_return : true, + join_vars : true, + sequences : false, + collapse_vars : false, + reduce_vars : true, + } + input: { + function foo(rot) { + const rotTol = 5; + if (rot < -rotTol || rot > rotTol) + bar(); + baz(); + } + } + expect: { + function foo(rot) { + (rot < -5 || rot > 5) && bar(); + baz(); + } + } +} + +issue_1194: { + options = { + evaluate : true, + booleans : true, + comparisons : true, + dead_code : true, + conditionals : true, + side_effects : true, + unused : true, + hoist_funs : true, + if_return : true, + join_vars : true, + sequences : false, + collapse_vars : false, + reduce_vars : true, + } + input: { + function f1() {const a = "X"; return a + a;} + function f2() {const aa = "X"; return aa + aa;} + function f3() {const aaa = "X"; return aaa + aaa;} + } + expect: { + function f1(){return"XX"} + function f2(){return"XX"} + function f3(){return"XX"} + } +} + +issue_1396: { + options = { + evaluate : true, + booleans : true, + comparisons : true, + dead_code : true, + conditionals : true, + side_effects : true, + unused : true, + hoist_funs : true, + if_return : true, + join_vars : true, + sequences : false, + collapse_vars : false, + reduce_vars : true, + } + input: { + function foo(a) { + const VALUE = 1; + console.log(2 | VALUE); + console.log(VALUE + 1); + console.log(VALUE); + console.log(a & VALUE); + } + function bar() { + const s = "01234567890123456789"; + console.log(s + s + s + s + s); + + const CONSTANT = "abc"; + console.log(CONSTANT + CONSTANT + CONSTANT + CONSTANT + CONSTANT); + } + } + expect: { + function foo(a) { + console.log(3); + console.log(2); + console.log(1); + console.log(1 & a); + } + function bar() { + const s = "01234567890123456789"; + console.log(s + s + s + s + s); + + console.log("abcabcabcabcabc"); + } + } +} + +unused_regexp_literal: { + options = { + evaluate : true, + booleans : true, + comparisons : true, + dead_code : true, + conditionals : true, + side_effects : true, + unused : true, + hoist_funs : true, + if_return : true, + join_vars : true, + sequences : false, + collapse_vars : false, + } + input: { + function f(){ var a = /b/; } + } + expect: { + function f(){} + } +} + +regexp_literal_not_const: { + options = { + evaluate : true, + booleans : true, + comparisons : true, + dead_code : true, + conditionals : true, + side_effects : true, + unused : true, + hoist_funs : true, + if_return : true, + join_vars : true, + sequences : false, + collapse_vars : false, + reduce_vars : true, + } + input: { + (function(){ + var result; + const s = 'acdabcdeabbb'; + const REGEXP_LITERAL = /ab*/g; + while (result = REGEXP_LITERAL.exec(s)) { + console.log(result[0]); + } + })(); + } + expect: { + (function() { + var result; + const REGEXP_LITERAL = /ab*/g; + while (result = REGEXP_LITERAL.exec("acdabcdeabbb")) console.log(result[0]); + })(); + } + expect_stdout: true +} diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js index 00dac06973c..af28e253133 100644 --- a/test/compress/dead-code.js +++ b/test/compress/dead-code.js @@ -90,6 +90,131 @@ dead_code_constant_boolean_should_warn_more: { expect_stdout: true } +dead_code_const_declaration: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true, + reduce_vars : true, + }; + input: { + var unused; + const CONST_FOO = false; + if (CONST_FOO) { + console.log("unreachable"); + var moo; + function bar() {} + } + } + expect: { + var unused; + const CONST_FOO = !1; + var moo; + function bar() {} + } + expect_stdout: true +} + +dead_code_const_annotation: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true, + reduce_vars : true, + toplevel : true, + }; + input: { + var unused; + /** @const */ var CONST_FOO_ANN = false; + if (CONST_FOO_ANN) { + console.log("unreachable"); + var moo; + function bar() {} + } + } + expect: { + var unused; + var CONST_FOO_ANN = !1; + var moo; + function bar() {} + } + expect_stdout: true +} + +dead_code_const_annotation_regex: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true + }; + input: { + var unused; + // @constraint this shouldn't be a constant + var CONST_FOO_ANN = false; + if (CONST_FOO_ANN) { + console.log("reachable"); + } + } + expect: { + var unused; + var CONST_FOO_ANN = !1; + CONST_FOO_ANN && console.log('reachable'); + } + expect_stdout: true +} + +dead_code_const_annotation_complex_scope: { + options = { + dead_code : true, + loops : true, + booleans : true, + conditionals : true, + evaluate : true, + reduce_vars : true, + toplevel : true, + }; + input: { + var unused_var; + /** @const */ var test = 'test'; + // @const + var CONST_FOO_ANN = false; + var unused_var_2; + if (CONST_FOO_ANN) { + console.log("unreachable"); + var moo; + function bar() {} + } + if (test === 'test') { + var beef = 'good'; + /** @const */ var meat = 'beef'; + var pork = 'bad'; + if (meat === 'pork') { + console.log('also unreachable'); + } else if (pork === 'good') { + console.log('reached, not const'); + } + } + } + expect: { + var unused_var; + var test = 'test'; + var CONST_FOO_ANN = !1; + var unused_var_2; + var moo; + function bar() {} + var beef = 'good'; + var meat = 'beef'; + var pork = 'bad'; + } + expect_stdout: true +} + try_catch_finally: { options = { conditionals: true, diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 2ef6f796fd3..96bd336c24a 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -649,6 +649,37 @@ drop_value: { } } +const_assign: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + const b = 2; + return 1 + b; + } + + function g() { + const b = 2; + b = 3; + return 1 + b; + } + } + expect: { + function f() { + return 3; + } + + function g() { + const b = 2; + b = 3; + return 1 + b; + } + } +} + issue_1539: { options = { cascade: true, @@ -785,6 +816,10 @@ issue_1709: { var x = 1; return x; }(), + function y() { + const y = 2; + return y; + }(), function z() { function z() {} return z; @@ -797,6 +832,10 @@ issue_1709: { var x = 1; return x; }(), + function() { + const y = 2; + return y; + }(), function() { function z() {} return z; @@ -1108,3 +1147,28 @@ var_catch_toplevel: { }(); } } + +reassign_const: { + options = { + cascade: true, + sequences: true, + side_effects: true, + unused: true, + } + input: { + function f() { + const a = 1; + a = 2; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + return a = 2, a; + } + console.log(f()); + } + expect_stdout: true +} diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index b8077564562..585ee2b9921 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -645,17 +645,16 @@ call_args: { options = { evaluate: true, reduce_vars: true, - toplevel: true, } input: { - var a = 1; + const a = 1; console.log(a); +function(a) { return a; }(a); } expect: { - var a = 1; + const a = 1; console.log(1); +(1, 1); } @@ -667,17 +666,17 @@ call_args_drop_param: { evaluate: true, keep_fargs: false, reduce_vars: true, - toplevel: true, unused: true, } input: { - var a = 1; + const a = 1; console.log(a); +function(a) { return a; }(a, b); } expect: { + const a = 1; console.log(1); +(b, 1); } diff --git a/test/compress/global_defs.js b/test/compress/global_defs.js index bfd1d5f6dd7..f1ba8f323b8 100644 --- a/test/compress/global_defs.js +++ b/test/compress/global_defs.js @@ -120,7 +120,7 @@ mixed: { properties: true, } input: { - var FOO = { BAR: 0 }; + const FOO = { BAR: 0 }; console.log(FOO.BAR); console.log(++CONFIG.DEBUG); console.log(++CONFIG.VALUE); @@ -130,7 +130,7 @@ mixed: { console.log(CONFIG); } expect: { - var FOO = { BAR: 0 }; + const FOO = { BAR: 0 }; console.log("moo"); console.log(++CONFIG.DEBUG); console.log(++CONFIG.VALUE); diff --git a/test/compress/issue-1041.js b/test/compress/issue-1041.js index cc351405944..cdbc22cc4f6 100644 --- a/test/compress/issue-1041.js +++ b/test/compress/issue-1041.js @@ -1,3 +1,16 @@ +const_declaration: { + options = { + evaluate: true + }; + + input: { + const goog = goog || {}; + } + expect: { + const goog = goog || {}; + } +} + const_pragma: { options = { evaluate: true, diff --git a/test/compress/issue-1588.js b/test/compress/issue-1588.js index 187d9f6cf8a..4e20a21d3e5 100644 --- a/test/compress/issue-1588.js +++ b/test/compress/issue-1588.js @@ -85,3 +85,15 @@ unsafe_undefined: { } expect_stdout: true } + +runtime_error: { + input: { + const a = 1; + console.log(a++); + } + expect: { + const a = 1; + console.log(a++); + } + expect_stdout: true +} diff --git a/test/compress/issue-208.js b/test/compress/issue-208.js index faaf41398a8..fb9861f68b8 100644 --- a/test/compress/issue-208.js +++ b/test/compress/issue-208.js @@ -38,7 +38,7 @@ mixed: { } } input: { - var ENV = 3; + const ENV = 3; var FOO = 4; f(ENV * 10); --FOO; @@ -49,7 +49,7 @@ mixed: { x = DEBUG; } expect: { - var ENV = 3; + const ENV = 3; var FOO = 4; f(10); --FOO; @@ -60,7 +60,7 @@ mixed: { x = 0; } expect_warnings: [ - 'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,12]', + 'WARN: global_defs ENV redefined [test/compress/issue-208.js:41,14]', 'WARN: global_defs FOO redefined [test/compress/issue-208.js:42,12]', 'WARN: global_defs FOO redefined [test/compress/issue-208.js:44,10]', 'WARN: global_defs DEBUG redefined [test/compress/issue-208.js:45,8]', diff --git a/test/compress/loops.js b/test/compress/loops.js index 89c7e7e935e..4d354bcf8e3 100644 --- a/test/compress/loops.js +++ b/test/compress/loops.js @@ -146,6 +146,50 @@ parse_do_while_without_semicolon: { } } + +keep_collapse_const_in_own_block_scope: { + options = { + join_vars: true, + loops: true + } + input: { + var i=2; + const c=5; + while(i--) + console.log(i); + console.log(c); + } + expect: { + var i=2; + const c=5; + for(;i--;) + console.log(i); + console.log(c); + } + expect_stdout: true +} + +keep_collapse_const_in_own_block_scope_2: { + options = { + join_vars: true, + loops: true + } + input: { + const c=5; + var i=2; // Moves to loop, while it did not in previous test + while(i--) + console.log(i); + console.log(c); + } + expect: { + const c=5; + for(var i=2;i--;) + console.log(i); + console.log(c); + } + expect_stdout: true +} + evaluate: { options = { loops: true, diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 2a44492ad0f..430e3797e2c 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -2190,11 +2190,10 @@ issue_1814_1: { options = { evaluate: true, reduce_vars: true, - toplevel: true, unused: true, } input: { - var a = 42; + const a = 42; !function() { var b = a; !function(a) { @@ -2203,6 +2202,7 @@ issue_1814_1: { }(); } expect: { + const a = 42; !function() { !function(a) { console.log(a++, 42); @@ -2216,11 +2216,10 @@ issue_1814_2: { options = { evaluate: true, reduce_vars: true, - toplevel: true, unused: true, } input: { - var a = "32"; + const a = "32"; !function() { var b = a + 1; !function(a) { @@ -2229,6 +2228,7 @@ issue_1814_2: { }(); } expect: { + const a = "32"; !function() { !function(a) { console.log(a++, "321"); diff --git a/test/compress/sequences.js b/test/compress/sequences.js index 104925655b5..9edf627eb87 100644 --- a/test/compress/sequences.js +++ b/test/compress/sequences.js @@ -710,3 +710,27 @@ issue_27: { })(jQuery); } } + +reassign_const: { + options = { + cascade: true, + sequences: true, + side_effects: true, + } + input: { + function f() { + const a = 1; + a++; + return a; + } + console.log(f()); + } + expect: { + function f() { + const a = 1; + return a++, a; + } + console.log(f()); + } + expect_stdout: true +} diff --git a/test/input/invalid/const.js b/test/input/invalid/const.js new file mode 100644 index 00000000000..7a2bfd3d88c --- /dev/null +++ b/test/input/invalid/const.js @@ -0,0 +1,8 @@ +function f() { + const a; +} + +function g() { + "use strict"; + const a; +} diff --git a/test/mocha/cli.js b/test/mocha/cli.js index 38f61f395f2..8cf53ab4f4a 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -379,6 +379,21 @@ describe("bin/uglifyjs", function () { done(); }); }); + it("Should throw syntax error (const a)", function(done) { + var command = uglifyjscmd + ' test/input/invalid/const.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/const.js:7,11", + " const a;", + " ^", + "ERROR: Missing initializer in const declaration" + ].join("\n")); + done(); + }); + }); it("Should throw syntax error (delete x)", function(done) { var command = uglifyjscmd + ' test/input/invalid/delete.js';