From: Alex Lam S.L Date: Sun, 26 Mar 2017 17:30:21 +0000 (+0800) Subject: handle overlapped variable definitions (#1691) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=57ce5bd9e085546a5c1cb8dd4a3ea71ab6c56f26;p=UglifyJS.git handle overlapped variable definitions (#1691) Process variable definitions with or without assigned values against: - `arguments` - named function arguments - multiple definitions within same scope Essentially demote variable declarations with no value assignments. Also fixed invalid use of `AST_VarDef` over `arguments` - should use a member of `AST_SymbolDeclaration` instead. --- diff --git a/lib/compress.js b/lib/compress.js index 83486b61..590015ff 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -260,7 +260,7 @@ merge(Compressor.prototype, { if (node instanceof AST_SymbolRef) { var d = node.definition(); d.references.push(node); - if (!d.fixed || !is_safe(d) + if (d.fixed === undefined || !is_safe(d) || is_modified(node, 0, d.fixed instanceof AST_Lambda)) { d.fixed = false; } @@ -270,10 +270,10 @@ merge(Compressor.prototype, { } if (node instanceof AST_VarDef) { var d = node.name.definition(); - if (d.fixed === undefined) { - d.fixed = node.value || make_node(AST_Undefined, node); + if (d.fixed == null) { + d.fixed = node.value; mark_as_safe(d); - } else { + } else if (node.value) { d.fixed = false; } } @@ -357,7 +357,14 @@ merge(Compressor.prototype, { function is_safe(def) { for (var i = safe_ids.length, id = def.id; --i >= 0;) { - if (safe_ids[i][id]) return true; + if (safe_ids[i][id]) { + if (def.fixed == null) { + var orig = def.orig[0]; + if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false; + def.fixed = make_node(AST_Undefined, orig); + } + return true; + } } } diff --git a/lib/scope.js b/lib/scope.js index 483503ee..b255032c 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -100,15 +100,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ if (node instanceof AST_Catch) { var save_scope = scope; scope = new AST_Scope(node); - scope.init_scope_vars(); - scope.parent_scope = save_scope; + scope.init_scope_vars(save_scope); descend(); scope = save_scope; return true; } if (node instanceof AST_Scope) { - node.init_scope_vars(); - var save_scope = node.parent_scope = scope; + node.init_scope_vars(scope); + var save_scope = scope; var save_defun = defun; var save_labels = labels; defun = scope = node; @@ -243,23 +242,24 @@ AST_Toplevel.DEFMETHOD("def_global", function(node){ } }); -AST_Scope.DEFMETHOD("init_scope_vars", function(){ - this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) - this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) - this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement - this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` - this.parent_scope = null; // the parent scope - this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes - this.cname = -1; // the current index for mangling functions/variables +AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope){ + this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) + this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) + this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement + this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` + this.parent_scope = parent_scope; // the parent scope + this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes + this.cname = -1; // the current index for mangling functions/variables }); AST_Lambda.DEFMETHOD("init_scope_vars", function(){ AST_Scope.prototype.init_scope_vars.apply(this, arguments); this.uses_arguments = false; - - var symbol = new AST_VarDef({ name: "arguments", start: this.start, end: this.end }); - var def = new SymbolDef(this, this.variables.size(), symbol); - this.variables.set(symbol.name, def); + this.def_variable(new AST_SymbolVar({ + name: "arguments", + start: this.start, + end: this.end + })); }); AST_SymbolRef.DEFMETHOD("reference", function(options) { diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index f4dd68d2..87942ab9 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -425,7 +425,7 @@ iife_new: { expect_stdout: true } -multi_def: { +multi_def_1: { options = { evaluate: true, reduce_vars: true, @@ -435,7 +435,7 @@ multi_def: { if (a) var b = 1; else - var b = 2 + var b = 2; console.log(b + 1); } } @@ -444,7 +444,7 @@ multi_def: { if (a) var b = 1; else - var b = 2 + var b = 2; console.log(b + 1); } } @@ -479,6 +479,33 @@ multi_def_2: { } } +multi_def_3: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + function f(a) { + var b = 2; + if (a) + var b; + else + var b; + console.log(b + 1); + } + } + expect: { + function f(a) { + var b = 2; + if (a) + var b; + else + var b; + console.log(3); + } + } +} + use_before_var: { options = { evaluate: true, @@ -1571,3 +1598,259 @@ unary_delete: { } expect_stdout: true } + +redefine_arguments_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + var arguments = 42; + return typeof arguments; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + return"number"; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect_stdout: "object number undefined" +} + +redefine_arguments_2: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + var arguments = 42; + return typeof arguments; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect: { + console.log(function() { + var arguments; + return typeof arguments; + }(), function() { + return"number"; + }(), function(x) { + var arguments = x; + return typeof arguments; + }()); + } + expect_stdout: "object number undefined" +} + +redefine_arguments_3: { + options = { + evaluate: true, + keep_fargs: false, + passes: 3, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f() { + var arguments; + return typeof arguments; + } + function g() { + var arguments = 42; + return typeof arguments; + } + function h(x) { + var arguments = x; + return typeof arguments; + } + console.log(f(), g(), h()); + } + expect: { + console.log(function() { + var arguments; + return typeof arguments; + }(), "number", "undefined"); + } + expect_stdout: "object number undefined" +} + +redefine_farg_1: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + unused: true, + } + input: { + function f(a) { + var a; + return typeof a; + } + function g(a) { + var a = 42; + return typeof a; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect: { + function f(a) { + var a; + return typeof a; + } + function g() { + return"number"; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect_stdout: "object number undefined" +} + +redefine_farg_2: { + options = { + evaluate: true, + keep_fargs: false, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var a; + return typeof a; + } + function g(a) { + var a = 42; + return typeof a; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect: { + console.log(function(a) { + var a; + return typeof a; + }([]), function() { + return "number"; + }(),function(a, b) { + var a = b; + return typeof a; + }([])); + } + expect_stdout: "object number undefined" +} + +redefine_farg_3: { + options = { + evaluate: true, + keep_fargs: false, + passes: 3, + reduce_vars: true, + side_effects: true, + toplevel: true, + unused: true, + } + input: { + function f(a) { + var a; + return typeof a; + } + function g(a) { + var a = 42; + return typeof a; + } + function h(a, b) { + var a = b; + return typeof a; + } + console.log(f([]), g([]), h([])); + } + expect: { + console.log(function(a) { + var a; + return typeof a; + }([]), "number", function(a) { + var a = void 0; + return typeof a; + }([])); + } + expect_stdout: "object number undefined" +} + +delay_def: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + function f() { + return a; + var a; + } + function g() { + return a; + var a = 1; + } + console.log(f(), g()); + } + expect: { + function f() { + return a; + var a; + } + function g() { + return a; + var a = 1; + } + console.log(f(), g()); + } + expect_stdout: true +}