diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ast.js | 15 | ||||
-rw-r--r-- | lib/compress.js | 66 | ||||
-rw-r--r-- | lib/output.js | 5 | ||||
-rw-r--r-- | lib/parse.js | 16 | ||||
-rw-r--r-- | lib/scope.js | 2 |
5 files changed, 76 insertions, 28 deletions
@@ -754,6 +754,17 @@ var AST_Const = DEFNODE("Const", null, { }, }, AST_Definitions); +var AST_Let = DEFNODE("Let", null, { + $documentation: "A `let` statement", + _validate: function() { + this.definitions.forEach(function(node) { + if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]"); + if (!(node.name instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet"); + if (node.value != null) must_be_expression(node, "value"); + }); + }, +}, AST_Definitions); + var AST_Var = DEFNODE("Var", null, { $documentation: "A `var` statement", _validate: function() { @@ -1066,6 +1077,10 @@ var AST_SymbolConst = DEFNODE("SymbolConst", null, { $documentation: "Symbol defining a constant", }, AST_SymbolDeclaration); +var AST_SymbolLet = DEFNODE("SymbolLet", null, { + $documentation: "Symbol defining a lexical-scoped variable", +}, AST_SymbolDeclaration); + var AST_SymbolVar = DEFNODE("SymbolVar", null, { $documentation: "Symbol defining a variable", }, AST_SymbolDeclaration); diff --git a/lib/compress.js b/lib/compress.js index 015ee306..d4be93ee 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -97,6 +97,7 @@ function Compressor(options, false_by_default) { unsafe_regexp : false, unsafe_undefined: false, unused : !false_by_default, + varify : !false_by_default, }, true); var evaluate = this.options["evaluate"]; this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate; @@ -488,7 +489,9 @@ merge(Compressor.prototype, { if (!(declare || all(def.orig, function(sym) { return !(sym instanceof AST_SymbolConst); }))) return false; - if (def.fixed === undefined) return true; + if (def.fixed === undefined) return declare || all(def.orig, function(sym) { + return !(sym instanceof AST_SymbolLet); + }); if (def.fixed === null && def.safe_ids) { def.safe_ids[def.id] = false; delete def.safe_ids; @@ -1616,7 +1619,7 @@ merge(Compressor.prototype, { if (side_effects && may_modify(node)) return true; var def = node.definition(); return (in_try || def.scope.resolve() !== scope) && !all(def.orig, function(sym) { - return !(sym instanceof AST_SymbolConst); + return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet); }); } if (node instanceof AST_This) return symbol_in_lvalues(node, parent); @@ -2177,7 +2180,7 @@ merge(Compressor.prototype, { var stat = statements[i]; if (stat instanceof AST_BlockStatement) { if (all(stat.body, function(stat) { - return !(stat instanceof AST_Const); + return !(stat instanceof AST_Const || stat instanceof AST_Let); })) { CHANGED = true; eliminate_spurious_blocks(stat.body); @@ -2513,7 +2516,7 @@ merge(Compressor.prototype, { var line = block.body[i]; if (line instanceof AST_Var && declarations_only(line)) { decls.push(line); - } else if (stat || line instanceof AST_Const) { + } else if (stat || line instanceof AST_Const || line instanceof AST_Let) { return false; } else { stat = line; @@ -2829,7 +2832,7 @@ merge(Compressor.prototype, { function push(node) { if (block) { block.push(node); - if (node instanceof AST_Const) block.required = true; + if (node instanceof AST_Const || node instanceof AST_Let) block.required = true; } else { target.push(node); } @@ -4106,7 +4109,7 @@ merge(Compressor.prototype, { def(AST_SymbolDeclaration, return_false); def(AST_SymbolRef, function(compressor) { return !(this.is_declared(compressor) && all(this.definition().orig, function(sym) { - return !(sym instanceof AST_SymbolConst); + return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet); })); }); def(AST_This, return_false); @@ -4360,7 +4363,7 @@ merge(Compressor.prototype, { return make_node(AST_EmptyStatement, node); case 1: var stat = node.body[0]; - if (!(stat instanceof AST_Const)) return stat; + if (!(stat instanceof AST_Const || stat instanceof AST_Let)) return stat; } return node; } @@ -4460,10 +4463,7 @@ merge(Compressor.prototype, { } if (node instanceof AST_Const) { node.definitions.forEach(function(defn) { - var def = defn.name.definition(); - references[def.id] = false; - def = def.redefined(); - if (def) references[def.id] = false; + references[defn.name.definition().id] = false; defn.value.walk(tw); }); return true; @@ -4516,6 +4516,13 @@ merge(Compressor.prototype, { pop(); return true; } + if (node instanceof AST_Let) { + node.definitions.forEach(function(defn) { + references[defn.name.definition().id] = false; + if (defn.value) defn.value.walk(tw); + }); + return true; + } if (node instanceof AST_Scope) { push(); segment.block = node; @@ -4618,7 +4625,10 @@ merge(Compressor.prototype, { if (!tail_refs) continue; if (head_refs.start.block !== tail_refs.start.block || !mergeable(head_refs, tail_refs) - || head_refs.start.loop && !mergeable(tail_refs, head_refs)) { + || head_refs.start.loop && !mergeable(tail_refs, head_refs) + || !all(tail_refs, function(sym) { + return sym.scope.find_variable(def.name) === def; + })) { skipped.unshift(tail); continue; } @@ -4715,7 +4725,7 @@ merge(Compressor.prototype, { if (!(sym instanceof AST_SymbolRef)) return; if (compressor.exposed(sym.definition())) return; if (!all(sym.definition().orig, function(sym) { - return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLambda); + return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLambda || sym instanceof AST_SymbolLet); })) return; return sym; }; @@ -4956,7 +4966,7 @@ merge(Compressor.prototype, { } } else if (compressor.option("functions") && !compressor.option("ie8") - && !(node instanceof AST_Const) + && !(node instanceof AST_Const || node instanceof AST_Let) && var_defs.length == 1 && sym.assignments == 0 && def.value instanceof AST_Function @@ -5100,7 +5110,7 @@ merge(Compressor.prototype, { return in_list ? List.skip : make_node(AST_EmptyStatement, node); case 1: var stat = node.body[0]; - if (!(stat instanceof AST_Const)) return stat; + if (!(stat instanceof AST_Const || stat instanceof AST_Let)) return stat; } else if (node instanceof AST_For) { // Certain combination of unused name + side effect leads to invalid AST: // https://github.com/mishoo/UglifyJS/issues/44 @@ -5665,7 +5675,7 @@ merge(Compressor.prototype, { return ref.fixed_value() === right; }) && all(def.orig, function(sym) { - return !(sym instanceof AST_SymbolConst); + return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet); }); } }); @@ -5883,7 +5893,7 @@ merge(Compressor.prototype, { }); function drop_symbol(ref) { return all(ref.definition().orig, function(sym) { - return !(sym instanceof AST_SymbolConst); + return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet); }); } def(AST_SymbolRef, function(compressor) { @@ -6623,6 +6633,12 @@ merge(Compressor.prototype, { }); }); + AST_Let.DEFMETHOD("remove_initializers", function() { + this.definitions.forEach(function(def) { + def.value = null; + }); + }); + AST_Var.DEFMETHOD("remove_initializers", function() { this.definitions.forEach(function(def) { def.value = null; @@ -6650,20 +6666,17 @@ merge(Compressor.prototype, { return make_sequence(this, assignments); }); - OPT(AST_Const, function(self, compressor) { - return all(self.definitions, function(defn) { + function varify(self, compressor) { + return compressor.option("varify") && all(self.definitions, function(defn) { var node = defn.name; if (!node.fixed_value()) return false; var def = node.definition(); if (compressor.exposed(def)) return false; var scope = def.scope.resolve(); - if (def.scope === scope) return true; - if (scope instanceof AST_Toplevel) return !scope.variables.has(node.name) && !scope.globals.has(node.name); - var s = def.scope; - do { + for (var s = def.scope; s !== scope;) { s = s.parent_scope; if (s.var_names()[node.name]) return false; - } while (s !== scope); + } return true; }) ? make_node(AST_Var, self, { definitions: self.definitions.map(function(defn) { @@ -6678,7 +6691,10 @@ merge(Compressor.prototype, { }); }) }) : self; - }); + } + + OPT(AST_Const, varify); + OPT(AST_Let, varify); function lift_sequence_in_expression(node, compressor) { var exp = node.expression; diff --git a/lib/output.js b/lib/output.js index 70bd74be..8068648e 100644 --- a/lib/output.js +++ b/lib/output.js @@ -990,7 +990,7 @@ function OutputStream(options) { /* -----[ if ]----- */ function make_then(self, output) { var b = self.body; - if (output.option("braces") && !(b instanceof AST_Const) + if (output.option("braces") && !(b instanceof AST_Const || b instanceof AST_Let) || output.option("ie8") && b instanceof AST_Do) return make_block(b, output); // The squeezer replaces "block"-s that contain only a single @@ -1124,6 +1124,7 @@ function OutputStream(options) { }; } DEFPRINT(AST_Const, print_definitinos("const")); + DEFPRINT(AST_Let, print_definitinos("let")); DEFPRINT(AST_Var, print_definitinos("var")); function parenthesize_for_noin(node, output, noin) { @@ -1381,7 +1382,7 @@ function OutputStream(options) { }); function force_statement(stat, output) { - if (output.option("braces") && !(stat instanceof AST_Const)) { + if (output.option("braces") && !(stat instanceof AST_Const || stat instanceof AST_Let)) { make_block(stat, output); } else if (!stat || stat instanceof AST_EmptyStatement) { output.force_semicolon(); diff --git a/lib/parse.js b/lib/parse.js index 42914d7d..7b2c8887 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -44,7 +44,7 @@ "use strict"; -var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with"; +var KEYWORDS = "break case catch const continue debugger default delete do else finally for function if in instanceof let new return switch throw try typeof var void while with"; var KEYWORDS_ATOM = "false null true"; var RESERVED_WORDS = [ "abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile yield", @@ -880,6 +880,12 @@ function parse($TEXT, options) { next(); return if_(); + case "let": + next(); + var node = let_(); + semicolon(); + return node; + case "return": if (S.in_function == 0 && !options.bare_returns) croak("'return' outside of function"); @@ -1202,6 +1208,14 @@ function parse($TEXT, options) { }); }; + var let_ = function(no_in) { + return new AST_Let({ + start : prev(), + definitions : vardefs(AST_SymbolLet, no_in), + end : prev() + }); + }; + var var_ = function(no_in) { return new AST_Var({ start : prev(), diff --git a/lib/scope.js b/lib/scope.js index ef19a530..4abf4066 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -169,6 +169,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { } else if (node instanceof AST_SymbolLambda) { var def = defun.def_function(node, node.name == "arguments" ? undefined : defun); if (options.ie8) def.defun = defun.parent_scope.resolve(); + } else if (node instanceof AST_SymbolLet) { + scope.def_variable(node); } else if (node instanceof AST_SymbolVar) { defun.def_variable(node, null); entangle(defun, scope); |