improve reduce_vars and fix a bug
- update modified flag between compress() passes - support IIFE arguments - fix corner case with multiple definitions closes #1473
This commit is contained in:
@@ -108,7 +108,8 @@ merge(Compressor.prototype, {
|
|||||||
compress: function(node) {
|
compress: function(node) {
|
||||||
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) node.clear_opt_flags();
|
if (pass > 0 || this.option("reduce_vars"))
|
||||||
|
node.reset_opt_flags(this);
|
||||||
node = node.transform(this);
|
node = node.transform(this);
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
@@ -167,19 +168,45 @@ merge(Compressor.prototype, {
|
|||||||
return this.print_to_string() == node.print_to_string();
|
return this.print_to_string() == node.print_to_string();
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("clear_opt_flags", function(){
|
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor){
|
||||||
this.walk(new TreeWalker(function(node){
|
var reduce_vars = compressor.option("reduce_vars");
|
||||||
|
var tw = new TreeWalker(function(node){
|
||||||
|
if (reduce_vars && node instanceof AST_Scope) {
|
||||||
|
node.variables.each(function(def) {
|
||||||
|
delete def.modified;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
var d = node.definition();
|
var d = node.definition();
|
||||||
if (d && d.init) {
|
if (d.init) {
|
||||||
delete d.init._evaluated;
|
delete d.init._evaluated;
|
||||||
}
|
}
|
||||||
|
if (reduce_vars && (d.orig.length > 1 || isModified(node, 0))) {
|
||||||
|
d.modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}));
|
});
|
||||||
|
this.walk(tw);
|
||||||
|
|
||||||
|
function isModified(node, level) {
|
||||||
|
var parent = tw.parent(level);
|
||||||
|
if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
|
||||||
|
|| parent instanceof AST_Assign && parent.left === node
|
||||||
|
|| parent instanceof AST_Call && parent.expression === node) {
|
||||||
|
return true;
|
||||||
|
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
||||||
|
return isModified(parent, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function make_node(ctor, orig, props) {
|
function make_node(ctor, orig, props) {
|
||||||
@@ -459,7 +486,7 @@ merge(Compressor.prototype, {
|
|||||||
var_defs_removed = true;
|
var_defs_removed = true;
|
||||||
}
|
}
|
||||||
// Further optimize statement after substitution.
|
// Further optimize statement after substitution.
|
||||||
stat.clear_opt_flags();
|
stat.reset_opt_flags(compressor);
|
||||||
|
|
||||||
compressor.warn("Replacing " + (is_constant ? "constant" : "variable") +
|
compressor.warn("Replacing " + (is_constant ? "constant" : "variable") +
|
||||||
" " + var_name + " [{file}:{line},{col}]", node.start);
|
" " + var_name + " [{file}:{line},{col}]", node.start);
|
||||||
@@ -1158,7 +1185,7 @@ merge(Compressor.prototype, {
|
|||||||
this._evaluating = true;
|
this._evaluating = true;
|
||||||
try {
|
try {
|
||||||
var d = this.definition();
|
var d = this.definition();
|
||||||
if (d && (d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) {
|
if ((d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
if (!HOP(d.init, '_evaluated')) {
|
if (!HOP(d.init, '_evaluated')) {
|
||||||
d.init._evaluated = ev(d.init, compressor);
|
d.init._evaluated = ev(d.init, compressor);
|
||||||
|
|||||||
26
lib/scope.js
26
lib/scope.js
@@ -183,17 +183,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||||||
var func = null;
|
var func = null;
|
||||||
var globals = self.globals = new Dictionary();
|
var globals = self.globals = new Dictionary();
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
function isModified(node, level) {
|
|
||||||
var parent = tw.parent(level);
|
|
||||||
if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
|
|
||||||
|| parent instanceof AST_Assign && parent.left === node
|
|
||||||
|| parent instanceof AST_Call && parent.expression === node) {
|
|
||||||
return true;
|
|
||||||
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
|
||||||
return isModified(parent, level + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda) {
|
||||||
var prev_func = func;
|
var prev_func = func;
|
||||||
func = node;
|
func = node;
|
||||||
@@ -217,21 +206,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
|||||||
node.scope.uses_arguments = true;
|
node.scope.uses_arguments = true;
|
||||||
}
|
}
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
var g;
|
|
||||||
if (globals.has(name)) {
|
if (globals.has(name)) {
|
||||||
g = globals.get(name);
|
sym = globals.get(name);
|
||||||
} else {
|
} else {
|
||||||
g = new SymbolDef(self, globals.size(), node);
|
sym = new SymbolDef(self, globals.size(), node);
|
||||||
g.undeclared = true;
|
sym.undeclared = true;
|
||||||
g.global = true;
|
sym.global = true;
|
||||||
globals.set(name, g);
|
globals.set(name, sym);
|
||||||
}
|
}
|
||||||
sym = g;
|
|
||||||
}
|
}
|
||||||
node.thedef = sym;
|
node.thedef = sym;
|
||||||
if (isModified(node, 0)) {
|
|
||||||
sym.modified = true;
|
|
||||||
}
|
|
||||||
node.reference(options);
|
node.reference(options);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,8 +108,6 @@ modified: {
|
|||||||
}
|
}
|
||||||
console.log(a + b);
|
console.log(a + b);
|
||||||
console.log(b + c);
|
console.log(b + c);
|
||||||
// TODO: as "modified" is determined in "figure_out_scope",
|
|
||||||
// even "passes" wouldn't improve this any further
|
|
||||||
console.log(a + c);
|
console.log(a + c);
|
||||||
console.log(a + b + c);
|
console.log(a + b + c);
|
||||||
}
|
}
|
||||||
@@ -350,3 +348,125 @@ unsafe_evaluate_equality: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
passes: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
passes: 2,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
var a = 1, b = 2, c = 3;
|
||||||
|
if (a) {
|
||||||
|
b = c;
|
||||||
|
} else {
|
||||||
|
c = b;
|
||||||
|
}
|
||||||
|
console.log(a + b);
|
||||||
|
console.log(b + c);
|
||||||
|
console.log(a + c);
|
||||||
|
console.log(a + b + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
var b = 2, c = 3;
|
||||||
|
b = c;
|
||||||
|
console.log(1 + b);
|
||||||
|
console.log(b + 3);
|
||||||
|
console.log(4);
|
||||||
|
console.log(1 + b + 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iife: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!function(a, b, c) {
|
||||||
|
b++;
|
||||||
|
console.log(a - 1, b * 1, c + 2);
|
||||||
|
}(1, 2, 3);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function(a, b, c) {
|
||||||
|
b++;
|
||||||
|
console.log(0, 1 * b, 5);
|
||||||
|
}(1, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iife_new: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var A = new function(a, b, c) {
|
||||||
|
b++;
|
||||||
|
console.log(a - 1, b * 1, c + 2);
|
||||||
|
}(1, 2, 3);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var A = new function(a, b, c) {
|
||||||
|
b++;
|
||||||
|
console.log(0, 1 * b, 5);
|
||||||
|
}(1, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multi_def: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
if (a)
|
||||||
|
var b = 1;
|
||||||
|
else
|
||||||
|
var b = 2
|
||||||
|
console.log(b + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
if (a)
|
||||||
|
var b = 1;
|
||||||
|
else
|
||||||
|
var b = 2
|
||||||
|
console.log(b + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multi_def_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
if (code == 16)
|
||||||
|
var bitsLength = 2, bitsOffset = 3, what = len;
|
||||||
|
else if (code == 17)
|
||||||
|
var bitsLength = 3, bitsOffset = 3, what = (len = 0);
|
||||||
|
else if (code == 18)
|
||||||
|
var bitsLength = 7, bitsOffset = 11, what = (len = 0);
|
||||||
|
var repeatLength = this.getBits(bitsLength) + bitsOffset;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
if (16 == code)
|
||||||
|
var bitsLength = 2, bitsOffset = 3, what = len;
|
||||||
|
else if (17 == code)
|
||||||
|
var bitsLength = 3, bitsOffset = 3, what = (len = 0);
|
||||||
|
else if (18 == code)
|
||||||
|
var bitsLength = 7, bitsOffset = 11, what = (len = 0);
|
||||||
|
var repeatLength = this.getBits(bitsLength) + bitsOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user