reduce memory pressure via bit fields (#5203)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 6 Dec 2021 03:30:05 +0000 (03:30 +0000)
committerGitHub <noreply@github.com>
Mon, 6 Dec 2021 03:30:05 +0000 (11:30 +0800)
bin/uglifyjs
lib/ast.js
lib/compress.js
lib/output.js
lib/parse.js
lib/scope.js
lib/utils.js
test/compress/annotations.js

index 6ee968f..726cd88 100755 (executable)
@@ -10,7 +10,9 @@ var info = require("../package.json");
 var path = require("path");
 var UglifyJS = require("../tools/node");
 
-var skip_keys = [ "cname", "fixed", "inlined", "parent_scope", "scope", "uses_eval", "uses_with" ];
+var skip_keys = [ "cname", "fixed", "inlined", "length_read", "parent_scope", "scope" ];
+var truthy_keys = [ "optional", "pure", "terminal", "uses_arguments", "uses_eval", "uses_with" ];
+
 var files = {};
 var options = {};
 var short_forms = {
@@ -430,7 +432,7 @@ function run() {
               case "thedef":
                 return symdef(value);
             }
-            if (skip_key(key)) return;
+            if (skip_property(key, value)) return;
             if (value instanceof UglifyJS.AST_Token) return;
             if (value instanceof UglifyJS.Dictionary) return;
             if (value instanceof UglifyJS.AST_Node) {
@@ -559,8 +561,10 @@ function parse_js(value, options, flag) {
     return options;
 }
 
-function skip_key(key) {
-    return skip_keys.indexOf(key) >= 0;
+function skip_property(key, value) {
+    return skip_keys.indexOf(key) >= 0
+        // only skip truthy_keys if their value is falsy
+        || truthy_keys.indexOf(key) >= 0 && !value;
 }
 
 function symdef(def) {
index 344d2ee..b21ea0c 100644 (file)
@@ -50,6 +50,8 @@ function DEFNODE(type, props, methods, base) {
     if (base && base.PROPS) props = props.concat(base.PROPS);
     var code = [
         "return function AST_", type, "(props){",
+        // not essential, but speeds up compress by a few percent
+        "this._bits=0;",
         "if(props){",
     ];
     props.forEach(function(prop) {
@@ -135,6 +137,52 @@ var AST_Node = DEFNODE("Node", "start end", {
     },
 }, null);
 
+DEF_BITPROPS(AST_Node, [
+    "_optimized",
+    "_squeezed",
+    // AST_Call
+    "call_only",
+    "collapse_scanning",
+    // AST_SymbolRef
+    "defined",
+    "evaluating",
+    "falsy",
+    // AST_SymbolRef
+    "in_arg",
+    // AST_Return
+    "in_bool",
+    // AST_SymbolRef
+    "is_undefined",
+    // AST_LambdaExpression
+    // AST_LambdaDefinition
+    "inlined",
+    // AST_Lambda
+    "length_read",
+    // AST_Yield
+    "nested",
+    // AST_Lambda
+    "new",
+    // AST_Call
+    // AST_PropAccess
+    "optional",
+    // AST_ClassProperty
+    "private",
+    // AST_Call
+    "pure",
+    // AST_Assign
+    "redundant",
+    // AST_ClassProperty
+    "static",
+    // AST_Call
+    // AST_PropAccess
+    "terminal",
+    "truthy",
+    // AST_Scope
+    "uses_eval",
+    // AST_Scope
+    "uses_with",
+]);
+
 (AST_Node.log_function = function(fn, verbose) {
     if (typeof fn != "function") {
         AST_Node.info = AST_Node.warn = noop;
@@ -549,7 +597,7 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest uses_arguments", {
         argnames: "[(AST_DefaultValue|AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
         length_read: "[boolean/S] whether length property of this function is accessed",
         rest: "[(AST_Destructured|AST_SymbolFunarg)?] rest parameter, or null if absent",
-        uses_arguments: "[boolean/S] whether this function accesses the arguments array",
+        uses_arguments: "[boolean|number/S] whether this function accesses the arguments array",
     },
     each_argname: function(visit) {
         var tw = new TreeWalker(function(node) {
@@ -1295,7 +1343,7 @@ var AST_Call = DEFNODE("Call", "args expression optional pure terminal", {
         args: "[AST_Node*] array of arguments",
         expression: "[AST_Node] expression to invoke as function",
         optional: "[boolean] whether the expression is optional chaining",
-        pure: "[string/S] marker for side-effect-free call expression",
+        pure: "[boolean/S] marker for side-effect-free call expression",
         terminal: "[boolean] whether the chain has ended",
     },
     walk: function(visitor) {
index 1a1d969..27878f1 100644 (file)
@@ -898,7 +898,7 @@ Compressor.prototype.compress = function(node) {
                 if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
                     right.walk(tw);
                     walk_prop(left);
-                    node.__drop = true;
+                    node.redundant = true;
                     return true;
                 }
                 if (ld && right instanceof AST_LambdaExpression) {
@@ -2274,7 +2274,7 @@ Compressor.prototype.compress = function(node) {
                     stop_if_hit = if_hit;
                     stop_after = after;
                     can_replace = replace;
-                    delete fn.collapse_scanning;
+                    fn.collapse_scanning = false;
                     if (!abort) return false;
                     abort = false;
                     return true;
@@ -4919,7 +4919,7 @@ Compressor.prototype.compress = function(node) {
                             }
                             if (node instanceof AST_Scope && node !== fn) return true;
                         }));
-                        delete fn.evaluating;
+                        fn.evaluating = false;
                         if (!found) return;
                     }
                     return this;
@@ -4935,7 +4935,7 @@ Compressor.prototype.compress = function(node) {
                     });
                     fn.evaluating = true;
                     val = val._eval(compressor, ignore_side_effects, cached, depth);
-                    delete fn.evaluating;
+                    fn.evaluating = false;
                 }
                 cached_args.forEach(function(node) {
                     delete node._eval;
@@ -8055,7 +8055,7 @@ Compressor.prototype.compress = function(node) {
             }) && all(fn.argnames, function(argname) {
                 return !argname.match_symbol(return_false);
             }) && !(fn.rest && fn.rest.match_symbol(return_false));
-            delete fn.new;
+            fn.new = false;
             return result;
         }
         function drop_class(self, compressor, first_in_statement) {
@@ -11640,7 +11640,7 @@ Compressor.prototype.compress = function(node) {
         if (compressor.option("dead_code")) {
             if (self.left instanceof AST_PropAccess) {
                 if (self.operator == "=") {
-                    if (self.__drop) {
+                    if (self.redundant) {
                         var exprs = [ self.left.expression ];
                         if (self.left instanceof AST_Sub) exprs.push(self.left.property);
                         exprs.push(self.right);
index d7927db..5c8f583 100644 (file)
@@ -1465,7 +1465,7 @@ function OutputStream(options) {
             parent = output.parent(level++);
             if (parent instanceof AST_Call && parent.expression === node) return;
         } while (parent instanceof AST_PropAccess && parent.expression === node);
-        output.print(typeof self.pure == "string" ? "/*" + self.pure + "*/" : "/*@__PURE__*/");
+        output.print("/*@__PURE__*/");
     }
     function print_call_args(self, output) {
         if (self.expression instanceof AST_Call || self.expression instanceof AST_Lambda) {
index 1b23dc2..982d9d4 100644 (file)
@@ -2316,9 +2316,8 @@ function parse($TEXT, options) {
             var comments = start.comments_before;
             var i = HOP(start, "comments_before_length") ? start.comments_before_length : comments.length;
             while (--i >= 0) {
-                var match = /[@#]__PURE__/.exec(comments[i].value);
-                if (match) {
-                    expr.pure = match[0];
+                if (/[@#]__PURE__/.test(comments[i].value)) {
+                    expr.pure = true;
                     break;
                 }
             }
index a6026e5..be0dc08 100644 (file)
@@ -44,9 +44,8 @@
 "use strict";
 
 function SymbolDef(id, scope, orig, init) {
+    this._bits = 0;
     this.eliminated = 0;
-    this.exported = false;
-    this.global = false;
     this.id = id;
     this.init = init;
     this.mangled_name = null;
@@ -55,7 +54,6 @@ function SymbolDef(id, scope, orig, init) {
     this.references = [];
     this.replaced = 0;
     this.scope = scope;
-    this.undeclared = false;
 }
 
 SymbolDef.prototype = {
@@ -104,6 +102,15 @@ SymbolDef.prototype = {
     },
 };
 
+DEF_BITPROPS(SymbolDef, [
+    "const_redefs",
+    "cross_loop",
+    "direct_access",
+    "exported",
+    "global",
+    "undeclared",
+]);
+
 var unary_side_effects = makePredicate("delete ++ --");
 
 function is_lhs(node, parent) {
index 4c46584..5712257 100644 (file)
@@ -273,3 +273,21 @@ function first_in_statement(stack, arrow, export_default) {
         return false;
     }
 }
+
+function DEF_BITPROPS(ctor, props) {
+    if (props.length > 31) throw new Error("Too many properties: " + props.length + "\n" + props.join(", "));
+    props.forEach(function(name, pos) {
+        var mask = 1 << pos;
+        Object.defineProperty(ctor.prototype, name, {
+            get: function() {
+                return !!(this._bits & mask);
+            },
+            set: function(val) {
+                if (val)
+                    this._bits |= mask;
+                else
+                    this._bits &= ~mask;
+            },
+        });
+    });
+}
index e85c8d8..4fdbbd8 100644 (file)
@@ -442,9 +442,9 @@ compress_annotations_disabled_output_annotations_enabled: {
     }
     expect_exact: [
         "/*@__PURE__*/a(3),",
-        "/*#__PURE__*/b(5),",
+        "/*@__PURE__*/b(5),",
         "c(side_effect),",
-        "/*#__PURE__*/d(effect());",
+        "/*@__PURE__*/d(effect());",
     ]
 }