improve `compress` (#3814)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 23 Apr 2020 22:50:53 +0000 (23:50 +0100)
committerGitHub <noreply@github.com>
Thu, 23 Apr 2020 22:50:53 +0000 (06:50 +0800)
- avoid identifier overflow through consecutive API calls
- simplify `reduce_vars`
- enhance `unsafe` `evaluate`

lib/compress.js
lib/scope.js
test/compress/reduce_vars.js

index 7c2077b..9420d8d 100644 (file)
@@ -460,7 +460,7 @@ merge(Compressor.prototype, {
             return def.fixed instanceof AST_Defun;
         }
 
-        function safe_to_assign(tw, def, scope, value) {
+        function safe_to_assign(tw, def, value) {
             if (def.fixed === undefined) return true;
             if (def.fixed === null && def.safe_ids) {
                 def.safe_ids[def.id] = false;
@@ -471,11 +471,8 @@ merge(Compressor.prototype, {
             if (!safe_to_read(tw, def)) return false;
             if (def.fixed === false) return false;
             if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
-            if (def.fixed instanceof AST_Defun) {
-                return value instanceof AST_Node && def.fixed.parent_scope === scope;
-            }
             return all(def.orig, function(sym) {
-                return !(sym instanceof AST_SymbolDefun || sym instanceof AST_SymbolLambda);
+                return !(sym instanceof AST_SymbolLambda);
             });
         }
 
@@ -557,7 +554,7 @@ merge(Compressor.prototype, {
             }
             if (sym.fixed) delete sym.fixed;
             var d = sym.definition();
-            var safe = safe_to_assign(tw, d, sym.scope, node.right);
+            var safe = safe_to_assign(tw, d, node.right);
             d.assignments++;
             var fixed = d.fixed;
             if (!fixed && node.operator != "=") return;
@@ -820,7 +817,7 @@ merge(Compressor.prototype, {
             }
             if (exp.fixed) delete exp.fixed;
             var d = exp.definition();
-            var safe = safe_to_assign(tw, d, exp.scope, true);
+            var safe = safe_to_assign(tw, d, true);
             d.assignments++;
             var fixed = d.fixed;
             if (!fixed) return;
@@ -846,7 +843,7 @@ merge(Compressor.prototype, {
             var node = this;
             var d = node.name.definition();
             if (node.value) {
-                if (safe_to_assign(tw, d, node.name.scope, node.value)) {
+                if (safe_to_assign(tw, d, node.value)) {
                     d.fixed = function() {
                         return node.value;
                     };
@@ -3195,7 +3192,7 @@ merge(Compressor.prototype, {
         def(AST_Statement, function() {
             throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
         });
-        def(AST_Lambda, return_this);
+        def(AST_Accessor, return_this);
         def(AST_Node, return_this);
         def(AST_Constant, function() {
             return this.value;
@@ -3213,7 +3210,7 @@ merge(Compressor.prototype, {
             var value = node._eval(compressor, ignore_side_effects, cached, depth);
             return value === node ? this : value;
         });
-        def(AST_Function, function(compressor) {
+        def(AST_Lambda, function(compressor) {
             if (compressor.option("unsafe")) {
                 var fn = function() {};
                 fn.node = this;
index ad33c5e..adbfe4a 100644 (file)
 
 "use strict";
 
-function SymbolDef(scope, orig, init) {
+function SymbolDef(id, scope, orig, init) {
+    this.eliminated = 0;
+    this.global = false;
+    this.id = id;
+    this.init = init;
+    this.lambda = orig instanceof AST_SymbolLambda;
+    this.mangled_name = null;
     this.name = orig.name;
     this.orig = [ orig ];
-    this.init = init;
-    this.eliminated = 0;
-    this.scope = scope;
     this.references = [];
     this.replaced = 0;
-    this.global = false;
-    this.mangled_name = null;
+    this.scope = scope;
     this.undeclared = false;
-    this.id = SymbolDef.next_id++;
-    this.lambda = orig instanceof AST_SymbolLambda;
 }
 
-SymbolDef.next_id = 1;
-
 SymbolDef.prototype = {
     unmangleable: function(options) {
         return this.global && !options.toplevel
@@ -151,6 +149,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
             scope.def_variable(node).defun = defun;
         }
     });
+    self.next_def_id = 0;
     self.walk(tw);
 
     // pass 2: find back references and eval
@@ -240,12 +239,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
     }
 });
 
+AST_Scope.DEFMETHOD("make_def", function(orig, init) {
+    var top = this;
+    while (top.parent_scope) top = top.parent_scope;
+    return new SymbolDef(++top.next_def_id, this, orig, init);
+});
+
 AST_Toplevel.DEFMETHOD("def_global", function(node) {
     var globals = this.globals, name = node.name;
     if (globals.has(name)) {
         return globals.get(name);
     } else {
-        var g = new SymbolDef(this, node);
+        var g = this.make_def(node);
         g.undeclared = true;
         g.global = true;
         globals.set(name, g);
@@ -310,7 +315,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
         def.orig.push(symbol);
         if (def.init instanceof AST_Function) def.init = init;
     } else {
-        def = new SymbolDef(this, symbol, init);
+        def = this.make_def(symbol, init);
         this.variables.set(symbol.name, def);
         def.global = !this.parent_scope;
     }
index 6be1171..25108ca 100644 (file)
@@ -223,6 +223,25 @@ unsafe_evaluate: {
     expect_stdout: true
 }
 
+unsafe_evaluate_defun: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        unsafe: true,
+        unused: true,
+    }
+    input: {
+        console.log(function() {
+            function f() {}
+            return ++f;
+        }());
+    }
+    expect: {
+        console.log(NaN);
+    }
+    expect_stdout: "NaN"
+}
+
 unsafe_evaluate_side_effect_free_1: {
     options = {
         evaluate: true,