[Pkg-javascript-commits] [uglifyjs] 356/491: fix escape analysis on `AST_PropAccess` (#2636)

Jonas Smedegaard dr at jones.dk
Wed Feb 14 19:51:52 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 c07ea17c015cb2e0fa3f48927751186ecb421481
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date:   Sun Dec 24 00:36:46 2017 +0800

    fix escape analysis on `AST_PropAccess` (#2636)
---
 lib/compress.js              | 95 +++++++++++++++++++++-----------------------
 test/compress/reduce_vars.js | 31 +++++++++++++++
 2 files changed, 76 insertions(+), 50 deletions(-)

diff --git a/lib/compress.js b/lib/compress.js
index 85cc6dd..5328b51 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -418,29 +418,27 @@ merge(Compressor.prototype, {
             }
         }
 
-        function mark_escaped(tw, d, scope, node, value, level) {
+        function mark_escaped(tw, d, scope, node, value, level, depth) {
             var parent = tw.parent(level);
-            if (value) {
-                if (value.is_constant()) return;
-                if (level > 0 && value.is_constant_expression(scope)) return;
-            }
+            if (value && value.is_constant()) return;
             if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
                 || parent instanceof AST_Call && node !== parent.expression
                 || parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
                 || parent instanceof AST_VarDef && node === parent.value) {
-                d.escaped = true;
+                if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
+                if (!d.escaped || d.escaped > depth) d.escaped = depth;
                 return;
             } else if (parent instanceof AST_Array
                 || parent instanceof AST_Binary && lazy_op(parent.operator)
                 || parent instanceof AST_Conditional && node !== parent.condition
                 || parent instanceof AST_Sequence && node === parent.tail_node()) {
-                mark_escaped(tw, d, scope, parent, parent, level + 1);
+                mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
             } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
                 var obj = tw.parent(level + 1);
-                mark_escaped(tw, d, scope, obj, obj, level + 2);
+                mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
             } else if (parent instanceof AST_PropAccess && node === parent.expression) {
                 value = read_property(value, parent.property);
-                mark_escaped(tw, d, scope, parent, value, level + 1);
+                mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
                 if (value) return;
             }
             if (level == 0) d.direct_access = true;
@@ -637,7 +635,7 @@ merge(Compressor.prototype, {
                     }
                 }
             }
-            mark_escaped(tw, d, this.scope, this, value, 0);
+            mark_escaped(tw, d, this.scope, this, value, 0, 1);
         });
         def(AST_Toplevel, function(tw, descend, compressor) {
             this.globals.each(function(def) {
@@ -1909,7 +1907,7 @@ merge(Compressor.prototype, {
         // descendant of AST_Node.
         AST_Node.DEFMETHOD("evaluate", function(compressor){
             if (!compressor.option("evaluate")) return this;
-            var val = this._eval(compressor);
+            var val = this._eval(compressor, 1);
             return !val || val instanceof RegExp || typeof val != "object" ? val : this;
         });
         var unaryPrefix = makePredicate("! ~ - + void");
@@ -1928,22 +1926,17 @@ merge(Compressor.prototype, {
             throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
         });
         def(AST_Lambda, return_this);
-        function ev(node, compressor) {
-            if (!compressor) throw new Error("Compressor must be passed");
-
-            return node._eval(compressor);
-        };
         def(AST_Node, return_this);
         def(AST_Constant, function(){
             return this.getValue();
         });
-        def(AST_Array, function(compressor){
+        def(AST_Array, function(compressor, depth) {
             if (compressor.option("unsafe")) {
                 var elements = [];
                 for (var i = 0, len = this.elements.length; i < len; i++) {
                     var element = this.elements[i];
                     if (element instanceof AST_Function) continue;
-                    var value = ev(element, compressor);
+                    var value = element._eval(compressor, depth);
                     if (element === value) return this;
                     elements.push(value);
                 }
@@ -1951,7 +1944,7 @@ merge(Compressor.prototype, {
             }
             return this;
         });
-        def(AST_Object, function(compressor){
+        def(AST_Object, function(compressor, depth) {
             if (compressor.option("unsafe")) {
                 var val = {};
                 for (var i = 0, len = this.properties.length; i < len; i++) {
@@ -1960,21 +1953,21 @@ merge(Compressor.prototype, {
                     if (key instanceof AST_Symbol) {
                         key = key.name;
                     } else if (key instanceof AST_Node) {
-                        key = ev(key, compressor);
+                        key = key._eval(compressor, depth);
                         if (key === prop.key) return this;
                     }
                     if (typeof Object.prototype[key] === 'function') {
                         return this;
                     }
                     if (prop.value instanceof AST_Function) continue;
-                    val[key] = ev(prop.value, compressor);
+                    val[key] = prop.value._eval(compressor, depth);
                     if (val[key] === prop.value) return this;
                 }
                 return val;
             }
             return this;
         });
-        def(AST_UnaryPrefix, function(compressor){
+        def(AST_UnaryPrefix, function(compressor, depth) {
             var e = this.expression;
             // Function would be evaluated to an array and so typeof would
             // incorrectly return 'object'. Hence making is a special case.
@@ -1985,7 +1978,7 @@ merge(Compressor.prototype, {
                         && e.fixed_value() instanceof AST_Lambda)) {
                 return typeof function(){};
             }
-            e = ev(e, compressor);
+            e = e._eval(compressor, depth);
             if (e === this.expression) return this;
             switch (this.operator) {
               case "!": return !e;
@@ -2001,10 +1994,10 @@ merge(Compressor.prototype, {
             }
             return this;
         });
-        def(AST_Binary, function(compressor){
-            var left = ev(this.left, compressor);
+        def(AST_Binary, function(compressor, depth) {
+            var left = this.left._eval(compressor, depth);
             if (left === this.left) return this;
-            var right = ev(this.right, compressor);
+            var right = this.right._eval(compressor, depth);
             if (right === this.right) return this;
             var result;
             switch (this.operator) {
@@ -2038,30 +2031,32 @@ merge(Compressor.prototype, {
             }
             return result;
         });
-        def(AST_Conditional, function(compressor){
-            var condition = ev(this.condition, compressor);
+        def(AST_Conditional, function(compressor, depth) {
+            var condition = this.condition._eval(compressor, depth);
             if (condition === this.condition) return this;
             var node = condition ? this.consequent : this.alternative;
-            var value = ev(node, compressor);
+            var value = node._eval(compressor, depth);
             return value === node ? this : value;
         });
-        def(AST_SymbolRef, function(compressor){
+        def(AST_SymbolRef, function(compressor, depth) {
             var fixed = this.fixed_value();
             if (!fixed) return this;
-            this._eval = return_this;
-            var value = ev(fixed, compressor);
-            if (value === fixed) {
+            var value;
+            if (HOP(fixed, "_eval")) {
+                value = fixed._eval();
+            } else {
+                this._eval = return_this;
+                value = fixed._eval(compressor, depth);
                 delete this._eval;
-                return this;
+                if (value === fixed) return this;
+                fixed._eval = function() {
+                    return value;
+                };
             }
-            if (!HOP(fixed, "_eval")) fixed._eval = function() {
-                return value;
-            };
-            if (value && typeof value == "object" && this.definition().escaped) {
-                delete this._eval;
-                return this;
+            if (value && typeof value == "object") {
+                var escaped = this.definition().escaped;
+                if (escaped && depth > escaped) return this;
             }
-            this._eval = fixed._eval;
             return value;
         });
         var global_objs = {
@@ -2095,11 +2090,11 @@ merge(Compressor.prototype, {
             ],
         };
         convert_to_predicate(static_values);
-        def(AST_PropAccess, function(compressor){
+        def(AST_PropAccess, function(compressor, depth) {
             if (compressor.option("unsafe")) {
                 var key = this.property;
                 if (key instanceof AST_Node) {
-                    key = ev(key, compressor);
+                    key = key._eval(compressor, depth);
                     if (key === this.property) return this;
                 }
                 var exp = this.expression;
@@ -2108,7 +2103,7 @@ merge(Compressor.prototype, {
                     if (!(static_values[exp.name] || return_false)(key)) return this;
                     val = global_objs[exp.name];
                 } else {
-                    val = ev(exp, compressor);
+                    val = exp._eval(compressor, depth + 1);
                     if (!val || val === exp || !HOP(val, key)) return this;
                 }
                 return val[key];
@@ -2186,12 +2181,12 @@ merge(Compressor.prototype, {
             ],
         };
         convert_to_predicate(static_fns);
-        def(AST_Call, function(compressor){
+        def(AST_Call, function(compressor, depth) {
             var exp = this.expression;
             if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
                 var key = exp.property;
                 if (key instanceof AST_Node) {
-                    key = ev(key, compressor);
+                    key = key._eval(compressor, depth);
                     if (key === exp.property) return this;
                 }
                 var val;
@@ -2200,13 +2195,13 @@ merge(Compressor.prototype, {
                     if (!(static_fns[e.name] || return_false)(key)) return this;
                     val = global_objs[e.name];
                 } else {
-                    val = ev(e, compressor);
+                    val = e._eval(compressor, depth + 1);
                     if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
                 }
                 var args = [];
                 for (var i = 0, len = this.args.length; i < len; i++) {
                     var arg = this.args[i];
-                    var value = ev(arg, compressor);
+                    var value = arg._eval(compressor, depth);
                     if (arg === value) return this;
                     args.push(value);
                 }
@@ -3083,7 +3078,7 @@ merge(Compressor.prototype, {
             if (node instanceof AST_VarDef) {
                 var sym = node.name, def, value;
                 if (sym.scope === self
-                    && !(def = sym.definition()).escaped
+                    && (def = sym.definition()).escaped != 1
                     && !def.single_use
                     && !def.direct_access
                     && !top_retain(def)
@@ -4698,7 +4693,7 @@ merge(Compressor.prototype, {
             if (d.single_use && fixed instanceof AST_Function) {
                 if (d.scope !== self.scope
                     && (!compressor.option("reduce_funcs")
-                        || d.escaped
+                        || d.escaped == 1
                         || fixed.inlined)) {
                     d.single_use = false;
                 } else if (recursive_ref(compressor, d)) {
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index bf1155b..2b2c77d 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -3423,6 +3423,37 @@ escaped_prop_1: {
 
 escaped_prop_2: {
     options = {
+        collapse_vars: true,
+        evaluate: true,
+        inline: true,
+        passes: 2,
+        pure_getters: "strict",
+        reduce_funcs: true,
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+        unsafe: true,
+        unused: true,
+    }
+    input: {
+        var obj = { o: { a: 1 } };
+        (function(o) {
+            o.a++;
+        })(obj.o);
+        (function(o) {
+            console.log(o.a);
+        })(obj.o);
+    }
+    expect: {
+        var obj = { o: { a: 1 } };
+        obj.o.a++;
+        console.log(obj.o.a);
+    }
+    expect_stdout: "2"
+}
+
+escaped_prop_3: {
+    options = {
         reduce_funcs: true,
         reduce_vars: true,
         toplevel: 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