[Pkg-javascript-commits] [uglifyjs] 26/491: enforce `toplevel` on other compress options (#1855)

Jonas Smedegaard dr at jones.dk
Wed Feb 14 19:51:18 UTC 2018


This is an automated email from the git hooks/post-receive script.

js pushed a commit to annotated tag debian/3.3.10-1
in repository uglifyjs.

commit 2cb55b2ad0119852bc8714401992724d4fdb224d
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Sun Apr 30 22:52:36 2017 +0800

    enforce `toplevel` on other compress options (#1855)
    
    Respect "funcs" and "vars" properly.
    
    fixes #1850
---
 lib/compress.js              | 521 ++++++++++++++++++++++---------------------
 test/compress/reduce_vars.js |  90 ++++++++
 2 files changed, 354 insertions(+), 257 deletions(-)

diff --git a/lib/compress.js b/lib/compress.js
index 0f4dd25..4e86a30 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -109,6 +109,14 @@ function Compressor(options, false_by_default) {
             return top_retain.indexOf(def.name) >= 0;
         };
     }
+    var toplevel = this.options["toplevel"];
+    if (typeof toplevel == "string") {
+        this.toplevel.funcs = /funcs/.test(toplevel);
+        this.toplevel.vars = /vars/.test(toplevel);
+    } else {
+        this.toplevel = toplevel ? return_true : return_false;
+        this.toplevel.funcs = this.toplevel.vars = toplevel;
+    }
     var sequences = this.options["sequences"];
     this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
     this.warnings_produced = {};
@@ -117,6 +125,12 @@ function Compressor(options, false_by_default) {
 Compressor.prototype = new TreeTransformer;
 merge(Compressor.prototype, {
     option: function(key) { return this.options[key] },
+    toplevel: function(def) {
+        for (var i = 0, len = def.orig.length; i < len; i++)
+            if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
+                return false;
+        return true;
+    },
     compress: function(node) {
         if (this.option("expression")) {
             node.process_expression(true);
@@ -249,7 +263,6 @@ merge(Compressor.prototype, {
 
     AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
         var reduce_vars = rescan && compressor.option("reduce_vars");
-        var toplevel = compressor.option("toplevel");
         var safe_ids = Object.create(null);
         var suppressor = new TreeWalker(function(node) {
             if (node instanceof AST_Symbol) {
@@ -310,7 +323,7 @@ merge(Compressor.prototype, {
                 }
                 if (node instanceof AST_Defun) {
                     var d = node.name.definition();
-                    if (!toplevel && d.global || safe_to_read(d)) {
+                    if (d.global && !compressor.toplevel(d) || safe_to_read(d)) {
                         d.fixed = false;
                     } else {
                         d.fixed = node;
@@ -469,7 +482,7 @@ merge(Compressor.prototype, {
         }
 
         function reset_def(def) {
-            if (toplevel || !def.global || def.orig[0] instanceof AST_SymbolConst) {
+            if (!def.global || def.orig[0] instanceof AST_SymbolConst || compressor.toplevel(def)) {
                 def.fixed = undefined;
             } else {
                 def.fixed = false;
@@ -643,7 +656,6 @@ merge(Compressor.prototype, {
             // in the statement with the var value and then erase the var definition.
 
             var scope = compressor.find_parent(AST_Scope);
-            var toplevel = compressor.option("toplevel");
             var stat_index;
             var prev_stat_index;
             var def_stat_index;
@@ -694,7 +706,8 @@ merge(Compressor.prototype, {
                     // Only interested in cases with just one reference to the variable.
                     var def = var_decl.name.definition();
                     if (def.references.length !== 1
-                        || var_name == "arguments" || (!toplevel && def.global)) {
+                        || var_name == "arguments"
+                        || def.global && !compressor.toplevel(def)) {
                         side_effects_encountered = true;
                         continue;
                     }
@@ -1825,289 +1838,283 @@ merge(Compressor.prototype, {
     });
 
     AST_Scope.DEFMETHOD("drop_unused", function(compressor){
+        if (!compressor.option("unused")) return;
+        if (compressor.has_directive("use asm")) return;
         var self = this;
-        if (compressor.has_directive("use asm")) return self;
-        var toplevel = compressor.option("toplevel");
-        if (compressor.option("unused")
-            && (!(self instanceof AST_Toplevel) || toplevel)
-            && !self.uses_eval
-            && !self.uses_with) {
-            var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
-            var drop_funcs = /funcs/.test(toplevel);
-            var drop_vars = /vars/.test(toplevel);
-            if (!(self instanceof AST_Toplevel) || toplevel == true) {
-                drop_funcs = drop_vars = true;
-            }
-            var in_use = [];
-            var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
-            if (self instanceof AST_Toplevel && compressor.top_retain) {
-                self.variables.each(function(def) {
-                    if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
-                        in_use_ids[def.id] = true;
-                        in_use.push(def);
+        if (self.uses_eval || self.uses_with) return;
+        var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
+        var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
+        if (!drop_funcs && !drop_vars) return;
+        var assign_as_unused = !/keep_assign/.test(compressor.option("unused"));
+        var in_use = [];
+        var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
+        if (self instanceof AST_Toplevel && compressor.top_retain) {
+            self.variables.each(function(def) {
+                if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
+                    in_use_ids[def.id] = true;
+                    in_use.push(def);
+                }
+            });
+        }
+        var var_defs_by_id = new Dictionary();
+        var initializations = new Dictionary();
+        // pass 1: find out which symbols are directly used in
+        // this scope (not in nested scopes).
+        var scope = this;
+        var tw = new TreeWalker(function(node, descend){
+            if (node !== self) {
+                if (node instanceof AST_Defun) {
+                    if (!drop_funcs && scope === self) {
+                        var node_def = node.name.definition();
+                        if (!(node_def.id in in_use_ids)) {
+                            in_use_ids[node_def.id] = true;
+                            in_use.push(node_def);
+                        }
                     }
-                });
-            }
-            var var_defs_by_id = new Dictionary();
-            var initializations = new Dictionary();
-            // pass 1: find out which symbols are directly used in
-            // this scope (not in nested scopes).
-            var scope = this;
-            var tw = new TreeWalker(function(node, descend){
-                if (node !== self) {
-                    if (node instanceof AST_Defun) {
-                        if (!drop_funcs && scope === self) {
-                            var node_def = node.name.definition();
+                    initializations.add(node.name.name, node);
+                    return true; // don't go in nested scopes
+                }
+                if (node instanceof AST_Definitions && scope === self) {
+                    node.definitions.forEach(function(def){
+                        var node_def = def.name.definition();
+                        if (def.name instanceof AST_SymbolVar) {
+                            var_defs_by_id.add(node_def.id, def);
+                        }
+                        if (!drop_vars) {
                             if (!(node_def.id in in_use_ids)) {
                                 in_use_ids[node_def.id] = true;
                                 in_use.push(node_def);
                             }
                         }
-                        initializations.add(node.name.name, node);
-                        return true; // don't go in nested scopes
-                    }
-                    if (node instanceof AST_Definitions && scope === self) {
-                        node.definitions.forEach(function(def){
-                            var node_def = def.name.definition();
-                            if (def.name instanceof AST_SymbolVar) {
-                                var_defs_by_id.add(node_def.id, def);
-                            }
-                            if (!drop_vars) {
-                                if (!(node_def.id in in_use_ids)) {
-                                    in_use_ids[node_def.id] = true;
-                                    in_use.push(node_def);
-                                }
-                            }
-                            if (def.value) {
-                                initializations.add(def.name.name, def.value);
-                                if (def.value.has_side_effects(compressor)) {
-                                    def.value.walk(tw);
-                                }
+                        if (def.value) {
+                            initializations.add(def.name.name, def.value);
+                            if (def.value.has_side_effects(compressor)) {
+                                def.value.walk(tw);
                             }
-                        });
-                        return true;
-                    }
-                    if (assign_as_unused
-                        && node instanceof AST_Assign
-                        && node.operator == "="
-                        && node.left instanceof AST_SymbolRef
-                        && scope === self) {
-                        node.right.walk(tw);
-                        return true;
-                    }
-                    if (node instanceof AST_SymbolRef) {
-                        var node_def = node.definition();
-                        if (!(node_def.id in in_use_ids)) {
-                            in_use_ids[node_def.id] = true;
-                            in_use.push(node_def);
                         }
-                        return true;
-                    }
-                    if (node instanceof AST_Scope) {
-                        var save_scope = scope;
-                        scope = node;
-                        descend();
-                        scope = save_scope;
-                        return true;
+                    });
+                    return true;
+                }
+                if (assign_as_unused
+                    && node instanceof AST_Assign
+                    && node.operator == "="
+                    && node.left instanceof AST_SymbolRef
+                    && scope === self) {
+                    node.right.walk(tw);
+                    return true;
+                }
+                if (node instanceof AST_SymbolRef) {
+                    var node_def = node.definition();
+                    if (!(node_def.id in in_use_ids)) {
+                        in_use_ids[node_def.id] = true;
+                        in_use.push(node_def);
                     }
+                    return true;
                 }
-            });
-            self.walk(tw);
-            // pass 2: for every used symbol we need to walk its
-            // initialization code to figure out if it uses other
-            // symbols (that may not be in_use).
-            for (var i = 0; i < in_use.length; ++i) {
-                in_use[i].orig.forEach(function(decl){
-                    // undeclared globals will be instanceof AST_SymbolRef
-                    var init = initializations.get(decl.name);
-                    if (init) init.forEach(function(init){
-                        var tw = new TreeWalker(function(node){
-                            if (node instanceof AST_SymbolRef) {
-                                var node_def = node.definition();
-                                if (!(node_def.id in in_use_ids)) {
-                                    in_use_ids[node_def.id] = true;
-                                    in_use.push(node_def);
-                                }
+                if (node instanceof AST_Scope) {
+                    var save_scope = scope;
+                    scope = node;
+                    descend();
+                    scope = save_scope;
+                    return true;
+                }
+            }
+        });
+        self.walk(tw);
+        // pass 2: for every used symbol we need to walk its
+        // initialization code to figure out if it uses other
+        // symbols (that may not be in_use).
+        for (var i = 0; i < in_use.length; ++i) {
+            in_use[i].orig.forEach(function(decl){
+                // undeclared globals will be instanceof AST_SymbolRef
+                var init = initializations.get(decl.name);
+                if (init) init.forEach(function(init){
+                    var tw = new TreeWalker(function(node){
+                        if (node instanceof AST_SymbolRef) {
+                            var node_def = node.definition();
+                            if (!(node_def.id in in_use_ids)) {
+                                in_use_ids[node_def.id] = true;
+                                in_use.push(node_def);
                             }
-                        });
-                        init.walk(tw);
+                        }
                     });
+                    init.walk(tw);
                 });
-            }
-            // pass 3: we should drop declarations not in_use
-            var tt = new TreeTransformer(
-                function before(node, descend, in_list) {
-                    if (node instanceof AST_Function
-                        && node.name
-                        && !compressor.option("keep_fnames")) {
-                        var def = node.name.definition();
-                        // any declarations with same name will overshadow
-                        // name of this anonymous function and can therefore
-                        // never be used anywhere
-                        if (!(def.id in in_use_ids) || def.orig.length > 1)
-                            node.name = null;
-                    }
-                    if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
-                        var trim = !compressor.option("keep_fargs");
-                        for (var a = node.argnames, i = a.length; --i >= 0;) {
-                            var sym = a[i];
-                            if (!(sym.definition().id in in_use_ids)) {
-                                sym.__unused = true;
-                                if (trim) {
-                                    a.pop();
-                                    compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
-                                }
-                            }
-                            else {
-                                trim = false;
+            });
+        }
+        // pass 3: we should drop declarations not in_use
+        var tt = new TreeTransformer(
+            function before(node, descend, in_list) {
+                if (node instanceof AST_Function
+                    && node.name
+                    && !compressor.option("keep_fnames")) {
+                    var def = node.name.definition();
+                    // any declarations with same name will overshadow
+                    // name of this anonymous function and can therefore
+                    // never be used anywhere
+                    if (!(def.id in in_use_ids) || def.orig.length > 1)
+                        node.name = null;
+                }
+                if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
+                    var trim = !compressor.option("keep_fargs");
+                    for (var a = node.argnames, i = a.length; --i >= 0;) {
+                        var sym = a[i];
+                        if (!(sym.definition().id in in_use_ids)) {
+                            sym.__unused = true;
+                            if (trim) {
+                                a.pop();
+                                compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
                             }
                         }
-                    }
-                    if (drop_funcs && node instanceof AST_Defun && node !== self) {
-                        if (!(node.name.definition().id in in_use_ids)) {
-                            compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
-                            return make_node(AST_EmptyStatement, node);
+                        else {
+                            trim = false;
                         }
-                        return node;
                     }
-                    if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
-                        // place uninitialized names at the start
-                        var body = [], head = [], tail = [];
-                        // for unused names whose initialization has
-                        // side effects, we can cascade the init. code
-                        // into the next one, or next statement.
-                        var side_effects = [];
-                        node.definitions.forEach(function(def) {
-                            if (def.value) def.value = def.value.transform(tt);
-                            var sym = def.name.definition();
-                            if (sym.id in in_use_ids) {
-                                if (def.name instanceof AST_SymbolVar) {
-                                    var var_defs = var_defs_by_id.get(sym.id);
-                                    if (var_defs.length > 1 && !def.value) {
-                                        compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
-                                        var_defs.splice(var_defs.indexOf(def), 1);
-                                        return;
-                                    }
+                }
+                if (drop_funcs && node instanceof AST_Defun && node !== self) {
+                    if (!(node.name.definition().id in in_use_ids)) {
+                        compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
+                        return make_node(AST_EmptyStatement, node);
+                    }
+                    return node;
+                }
+                if (drop_vars && node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn && tt.parent().init === node)) {
+                    // place uninitialized names at the start
+                    var body = [], head = [], tail = [];
+                    // for unused names whose initialization has
+                    // side effects, we can cascade the init. code
+                    // into the next one, or next statement.
+                    var side_effects = [];
+                    node.definitions.forEach(function(def) {
+                        if (def.value) def.value = def.value.transform(tt);
+                        var sym = def.name.definition();
+                        if (sym.id in in_use_ids) {
+                            if (def.name instanceof AST_SymbolVar) {
+                                var var_defs = var_defs_by_id.get(sym.id);
+                                if (var_defs.length > 1 && !def.value) {
+                                    compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
+                                    var_defs.splice(var_defs.indexOf(def), 1);
+                                    return;
                                 }
-                                if (def.value) {
-                                    if (side_effects.length > 0) {
-                                        if (tail.length > 0) {
-                                            merge_sequence(side_effects, def.value);
-                                            def.value = make_sequence(def.value, side_effects);
-                                        } else {
-                                            body.push(make_node(AST_SimpleStatement, node, {
-                                                body: make_sequence(node, side_effects)
-                                            }));
-                                        }
-                                        side_effects = [];
+                            }
+                            if (def.value) {
+                                if (side_effects.length > 0) {
+                                    if (tail.length > 0) {
+                                        merge_sequence(side_effects, def.value);
+                                        def.value = make_sequence(def.value, side_effects);
+                                    } else {
+                                        body.push(make_node(AST_SimpleStatement, node, {
+                                            body: make_sequence(node, side_effects)
+                                        }));
                                     }
-                                    tail.push(def);
-                                } else {
-                                    head.push(def);
+                                    side_effects = [];
                                 }
-                            } else if (sym.orig[0] instanceof AST_SymbolCatch) {
-                                var value = def.value && def.value.drop_side_effect_free(compressor);
-                                if (value) merge_sequence(side_effects, value);
-                                def.value = null;
-                                head.push(def);
+                                tail.push(def);
                             } else {
-                                var value = def.value && def.value.drop_side_effect_free(compressor);
-                                if (value) {
-                                    compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
-                                    merge_sequence(side_effects, value);
-                                } else {
-                                    compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
-                                }
+                                head.push(def);
                             }
-                        });
-                        if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
-                            var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
-                            if (var_defs.length > 1) {
-                                var def = tail.pop();
-                                compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
-                                var_defs.splice(var_defs.indexOf(def), 1);
-                                side_effects.unshift(make_node(AST_Assign, def, {
-                                    operator: "=",
-                                    left: make_node(AST_SymbolRef, def.name, def.name),
-                                    right: def.value
-                                }));
+                        } else if (sym.orig[0] instanceof AST_SymbolCatch) {
+                            var value = def.value && def.value.drop_side_effect_free(compressor);
+                            if (value) merge_sequence(side_effects, value);
+                            def.value = null;
+                            head.push(def);
+                        } else {
+                            var value = def.value && def.value.drop_side_effect_free(compressor);
+                            if (value) {
+                                compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
+                                merge_sequence(side_effects, value);
+                            } else {
+                                compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
                             }
                         }
-                        if (head.length > 0 || tail.length > 0) {
-                            node.definitions = head.concat(tail);
-                            body.push(node);
-                        }
-                        if (side_effects.length > 0) {
-                            body.push(make_node(AST_SimpleStatement, node, {
-                                body: make_sequence(node, side_effects)
+                    });
+                    if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
+                        var var_defs = var_defs_by_id.get(tail[0].name.definition().id);
+                        if (var_defs.length > 1) {
+                            var def = tail.pop();
+                            compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
+                            var_defs.splice(var_defs.indexOf(def), 1);
+                            side_effects.unshift(make_node(AST_Assign, def, {
+                                operator: "=",
+                                left: make_node(AST_SymbolRef, def.name, def.name),
+                                right: def.value
                             }));
                         }
-                        switch (body.length) {
-                          case 0:
-                            return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
-                          case 1:
-                            return body[0];
-                          default:
-                            return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
-                                body: body
-                            });
-                        }
-                    }
-                    if (drop_vars && assign_as_unused
-                        && node instanceof AST_Assign
-                        && node.operator == "="
-                        && node.left instanceof AST_SymbolRef) {
-                        var def = node.left.definition();
-                        if (!(def.id in in_use_ids)
-                            && self.variables.get(def.name) === def) {
-                            return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
-                        }
                     }
-                    // certain combination of unused name + side effect leads to:
-                    //    https://github.com/mishoo/UglifyJS2/issues/44
-                    //    https://github.com/mishoo/UglifyJS2/issues/1830
-                    //    https://github.com/mishoo/UglifyJS2/issues/1838
-                    // that's an invalid AST.
-                    // We fix it at this stage by moving the `var` outside the `for`.
-                    if (node instanceof AST_For) {
-                        descend(node, this);
-                        if (node.init instanceof AST_BlockStatement) {
-                            var block = node.init;
-                            node.init = block.body.pop();
-                            block.body.push(node);
-                            return in_list ? MAP.splice(block.body) : block;
-                        } else if (node.init instanceof AST_SimpleStatement) {
-                            node.init = node.init.body;
-                        } else if (is_empty(node.init)) {
-                            node.init = null;
-                        }
-                        return node;
+                    if (head.length > 0 || tail.length > 0) {
+                        node.definitions = head.concat(tail);
+                        body.push(node);
+                    }
+                    if (side_effects.length > 0) {
+                        body.push(make_node(AST_SimpleStatement, node, {
+                            body: make_sequence(node, side_effects)
+                        }));
+                    }
+                    switch (body.length) {
+                        case 0:
+                        return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
+                        case 1:
+                        return body[0];
+                        default:
+                        return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
+                            body: body
+                        });
                     }
-                    if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
-                        descend(node, this);
-                        if (node.body instanceof AST_BlockStatement) {
-                            var block = node.body;
-                            node.body = block.body.pop();
-                            block.body.push(node);
-                            return in_list ? MAP.splice(block.body) : block;
-                        }
-                        return node;
+                }
+                if (drop_vars && assign_as_unused
+                    && node instanceof AST_Assign
+                    && node.operator == "="
+                    && node.left instanceof AST_SymbolRef) {
+                    var def = node.left.definition();
+                    if (!(def.id in in_use_ids)
+                        && self.variables.get(def.name) === def) {
+                        return maintain_this_binding(tt.parent(), node, node.right.transform(tt));
+                    }
+                }
+                // certain combination of unused name + side effect leads to:
+                //    https://github.com/mishoo/UglifyJS2/issues/44
+                //    https://github.com/mishoo/UglifyJS2/issues/1830
+                //    https://github.com/mishoo/UglifyJS2/issues/1838
+                // that's an invalid AST.
+                // We fix it at this stage by moving the `var` outside the `for`.
+                if (node instanceof AST_For) {
+                    descend(node, this);
+                    if (node.init instanceof AST_BlockStatement) {
+                        var block = node.init;
+                        node.init = block.body.pop();
+                        block.body.push(node);
+                        return in_list ? MAP.splice(block.body) : block;
+                    } else if (node.init instanceof AST_SimpleStatement) {
+                        node.init = node.init.body;
+                    } else if (is_empty(node.init)) {
+                        node.init = null;
                     }
-                    if (node instanceof AST_Scope && node !== self)
-                        return node;
-
-                    function template(sym) {
-                        return {
-                            name : sym.name,
-                            file : sym.start.file,
-                            line : sym.start.line,
-                            col  : sym.start.col
-                        };
+                    return node;
+                }
+                if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
+                    descend(node, this);
+                    if (node.body instanceof AST_BlockStatement) {
+                        var block = node.body;
+                        node.body = block.body.pop();
+                        block.body.push(node);
+                        return in_list ? MAP.splice(block.body) : block;
                     }
+                    return node;
                 }
-            );
-            self.transform(tt);
-        }
+                if (node instanceof AST_Scope && node !== self)
+                    return node;
+
+                function template(sym) {
+                    return {
+                        name : sym.name,
+                        file : sym.start.file,
+                        line : sym.start.line,
+                        col  : sym.start.col
+                    };
+                }
+            }
+        );
+        self.transform(tt);
     });
 
     AST_Scope.DEFMETHOD("hoist_declarations", function(compressor){
@@ -3683,7 +3690,7 @@ merge(Compressor.prototype, {
                         }
                         var name = d.name.length;
                         var overhead = 0;
-                        if (compressor.option("unused") && (!d.global || compressor.option("toplevel"))) {
+                        if (compressor.option("unused") && (!d.global || compressor.toplevel(d))) {
                             overhead = (name + 2 + value) / d.references.length;
                         }
                         d.should_replace = value <= name + overhead ? fn : false;
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 57e2389..f7bdcb3 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -2327,3 +2327,93 @@ iife_assign: {
     }
     expect_stdout: "1"
 }
+
+issue_1850_1: {
+    options = {
+        reduce_vars: true,
+        toplevel: false,
+        unused: true,
+    }
+    input: {
+        function f() {
+            console.log(a, a, a);
+        }
+        var a = 1;
+        f();
+    }
+    expect: {
+        function f() {
+            console.log(a, a, a);
+        }
+        var a = 1;
+        f();
+    }
+    expect_stdout: true
+}
+
+issue_1850_2: {
+    options = {
+        reduce_vars: true,
+        toplevel: "funcs",
+        unused: true,
+    }
+    input: {
+        function f() {
+            console.log(a, a, a);
+        }
+        var a = 1;
+        f();
+    }
+    expect: {
+        var a = 1;
+        (function() {
+            console.log(a, a, a);
+        })();
+    }
+    expect_stdout: true
+}
+
+issue_1850_3: {
+    options = {
+        reduce_vars: true,
+        toplevel: "vars",
+        unused: true,
+    }
+    input: {
+        function f() {
+            console.log(a, a, a);
+        }
+        var a = 1;
+        f();
+    }
+    expect: {
+        function f() {
+            console.log(a, a, a);
+        }
+        var a = 1;
+        f();
+    }
+    expect_stdout: true
+}
+
+issue_1850_4: {
+    options = {
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        function f() {
+            console.log(a, a, a);
+        }
+        var a = 1;
+        f();
+    }
+    expect: {
+        var a = 1;
+        (function() {
+            console.log(a, a, a);
+        })();
+    }
+    expect_stdout: true
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/uglifyjs.git



More information about the Pkg-javascript-commits mailing list