consolidate evaluate & reduce_vars (#1505)

- improve marking efficiency
- apply smarter `const` replacement to `var`

fixes #1501
This commit is contained in:
Alex Lam S.L
2017-02-26 00:40:33 +08:00
committed by GitHub
parent 834f9f3924
commit 16cd5d57a5
5 changed files with 57 additions and 50 deletions

View File

@@ -118,7 +118,7 @@ merge(Compressor.prototype, {
var passes = +this.options.passes || 1; var passes = +this.options.passes || 1;
for (var pass = 0; pass < passes && pass < 3; ++pass) { for (var pass = 0; pass < passes && pass < 3; ++pass) {
if (pass > 0 || this.option("reduce_vars")) if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this); node.reset_opt_flags(this, true);
node = node.transform(this); node = node.transform(this);
} }
return node; return node;
@@ -177,28 +177,26 @@ merge(Compressor.prototype, {
return this.print_to_string() == node.print_to_string(); return this.print_to_string() == node.print_to_string();
}); });
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor){ AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
var reduce_vars = compressor.option("reduce_vars"); var reduce_vars = rescan && compressor.option("reduce_vars");
var unsafe = compressor.option("unsafe");
var tw = new TreeWalker(function(node){ var tw = new TreeWalker(function(node){
if (reduce_vars && node instanceof AST_Scope) { if (reduce_vars) {
node.variables.each(function(def) { if (node instanceof AST_Toplevel) node.globals.each(reset_def);
delete def.modified; if (node instanceof AST_Scope) node.variables.each(reset_def);
}); if (node instanceof AST_SymbolRef) {
} var d = node.definition();
if (node instanceof AST_SymbolRef) { d.references.push(node);
var d = node.definition(); if (!d.modified && (d.orig.length > 1 || isModified(node, 0))) {
if (d.init) { d.modified = true;
delete d.init._evaluated; }
} }
if (reduce_vars && (d.orig.length > 1 || isModified(node, 0))) { if (node instanceof AST_Call && node.expression instanceof AST_Function) {
d.modified = true; node.expression.argnames.forEach(function(arg, i) {
arg.definition().init = node.args[i] || make_node(AST_Undefined, node);
});
} }
} }
if (reduce_vars && node instanceof AST_Call && node.expression instanceof AST_Function) {
node.expression.argnames.forEach(function(arg, i) {
arg.definition().init = node.args[i] || make_node(AST_Undefined, node);
});
}
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) { if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
node._squeezed = false; node._squeezed = false;
node._optimized = false; node._optimized = false;
@@ -206,10 +204,18 @@ merge(Compressor.prototype, {
}); });
this.walk(tw); this.walk(tw);
function reset_def(def) {
def.modified = false;
def.references = [];
def.should_replace = undefined;
if (unsafe && def.init) {
def.init._evaluated = undefined;
}
}
function isModified(node, level) { function isModified(node, level) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--") if (isLHS(node, parent)
|| parent instanceof AST_Assign && parent.left === node
|| parent instanceof AST_Call && parent.expression === node) { || parent instanceof AST_Call && parent.expression === node) {
return true; return true;
} else if (parent instanceof AST_PropAccess && parent.expression === node) { } else if (parent instanceof AST_PropAccess && parent.expression === node) {
@@ -1254,7 +1260,7 @@ merge(Compressor.prototype, {
var d = this.definition(); var d = this.definition();
if (compressor.option("reduce_vars") && !d.modified && d.init) { if (compressor.option("reduce_vars") && !d.modified && d.init) {
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
if (!HOP(d.init, '_evaluated')) { if (d.init._evaluated === undefined) {
d.init._evaluated = ev(d.init, compressor); d.init._evaluated = ev(d.init, compressor);
} }
return d.init._evaluated; return d.init._evaluated;
@@ -2403,9 +2409,6 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Call, function(self, compressor){ OPT(AST_Call, function(self, compressor){
self.args = self.args.map(function(arg) {
return arg.evaluate(compressor)[0];
});
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
var exp = self.expression; var exp = self.expression;
if (exp instanceof AST_SymbolRef && exp.undeclared()) { if (exp instanceof AST_SymbolRef && exp.undeclared()) {
@@ -3025,18 +3028,24 @@ merge(Compressor.prototype, {
return make_node(AST_Infinity, self).transform(compressor); return make_node(AST_Infinity, self).transform(compressor);
} }
} }
if (compressor.option("evaluate") if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
&& compressor.option("reduce_vars")
&& !isLHS(self, compressor.parent())) {
var d = self.definition(); var d = self.definition();
if (d.constant && !d.modified && d.init && d.init.is_constant(compressor)) { if (!d.modified && d.init) {
var original_as_string = self.print_to_string(); if (d.should_replace === undefined) {
var const_node = make_node_from_constant(compressor, d.init.constant_value(compressor), self); var init = d.init.evaluate(compressor);
var const_node_as_string = const_node.print_to_string(); if (init.length > 1) {
var per_const_overhead = d.global || !d.references.length ? 0 var value = init[0].print_to_string().length;
: (d.name.length + 2 + const_node_as_string.length) / d.references.length; var name = d.name.length;
if (const_node_as_string.length <= original_as_string.length + per_const_overhead) var freq = d.references.length;
return const_node; var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
d.should_replace = value <= name + overhead ? init[0] : false;
} else {
d.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace;
}
} }
} }
return self; return self;

View File

@@ -51,7 +51,6 @@ function SymbolDef(scope, index, orig) {
this.global = false; this.global = false;
this.mangled_name = null; this.mangled_name = null;
this.undeclared = false; this.undeclared = false;
this.constant = false;
this.index = index; this.index = index;
this.id = SymbolDef.next_id++; this.id = SymbolDef.next_id++;
}; };
@@ -156,7 +155,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
else if (node instanceof AST_SymbolVar else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst) { || node instanceof AST_SymbolConst) {
var def = defun.def_variable(node); var def = defun.def_variable(node);
def.constant = node instanceof AST_SymbolConst;
def.init = tw.parent().value; def.init = tw.parent().value;
} }
else if (node instanceof AST_SymbolCatch) { else if (node instanceof AST_SymbolCatch) {

View File

@@ -615,7 +615,7 @@ call_args: {
const a = 1; const a = 1;
console.log(1); console.log(1);
+function(a) { +function(a) {
return a; return 1;
}(1); }(1);
} }
} }

View File

@@ -134,8 +134,8 @@ modified: {
} }
function f2() { function f2() {
var b = 2, c = 3; var b = 2;
b = c; b = 3;
console.log(1 + b); console.log(1 + b);
console.log(b + 3); console.log(b + 3);
console.log(4); console.log(4);
@@ -143,8 +143,8 @@ modified: {
} }
function f3() { function f3() {
var b = 2, c = 3; var b = 2;
b *= c; b *= 3;
console.log(1 + b); console.log(1 + b);
console.log(b + 3); console.log(b + 3);
console.log(4); console.log(4);
@@ -236,7 +236,7 @@ unsafe_evaluate_object: {
function f0(){ function f0(){
var a = 1; var a = 1;
var b = {}; var b = {};
b[a] = 2; b[1] = 2;
console.log(4); console.log(4);
} }
@@ -280,7 +280,7 @@ unsafe_evaluate_array: {
function f0(){ function f0(){
var a = 1; var a = 1;
var b = []; var b = [];
b[a] = 2; b[1] = 2;
console.log(4); console.log(4);
} }
@@ -373,8 +373,8 @@ passes: {
} }
expect: { expect: {
function f() { function f() {
var b = 2, c = 3; var b = 2;
b = c; b = 3;
console.log(1 + b); console.log(1 + b);
console.log(b + 3); console.log(b + 3);
console.log(4); console.log(4);

View File

@@ -7,7 +7,7 @@ describe("comment before constant", function() {
it("Should test comment before constant is retained and output after mangle.", function() { it("Should test comment before constant is retained and output after mangle.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true, fromString: true,
compress: { collapse_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {}, mangle: {},
output: { comments: true }, output: { comments: true },
}); });
@@ -17,9 +17,9 @@ describe("comment before constant", function() {
it("Should test code works when comments disabled.", function() { it("Should test code works when comments disabled.", function() {
var result = Uglify.minify(js, { var result = Uglify.minify(js, {
fromString: true, fromString: true,
compress: { collapse_vars: false }, compress: { collapse_vars: false, reduce_vars: false },
mangle: {}, mangle: {},
output: {}, output: { comments: false },
}); });
assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); assert.strictEqual(result.code, 'function f(){var n=!1;return n}');
}); });