aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-12-17 23:01:08 +0800
committerGitHub <noreply@github.com>2017-12-17 23:01:08 +0800
commit7918a50d52809a854d6808c7a97f87f8e256506e (patch)
tree638a22bdc8214ce1e3990e695ab4629304a9a53a /lib
parent21794c9b8d0102ed00a15d1e54314908c92e4a24 (diff)
downloadtracifyjs-7918a50d52809a854d6808c7a97f87f8e256506e.tar.gz
tracifyjs-7918a50d52809a854d6808c7a97f87f8e256506e.zip
improve `reset_opt_flags()` (#2610)
Diffstat (limited to 'lib')
-rw-r--r--lib/compress.js604
1 files changed, 311 insertions, 293 deletions
diff --git a/lib/compress.js b/lib/compress.js
index ebcae482..bccd7630 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -293,14 +293,12 @@ merge(Compressor.prototype, {
if (index >= 0) {
node.body[index] = node.body[index].transform(tt);
}
- }
- if (node instanceof AST_If) {
+ } else if (node instanceof AST_If) {
node.body = node.body.transform(tt);
if (node.alternative) {
node.alternative = node.alternative.transform(tt);
}
- }
- if (node instanceof AST_With) {
+ } else if (node instanceof AST_With) {
node.body = node.body.transform(tt);
}
return node;
@@ -308,262 +306,44 @@ merge(Compressor.prototype, {
self.transform(tt);
});
- AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
- var reduce_vars = compressor.option("reduce_vars");
- var unused = compressor.option("unused");
- // Stack of look-up tables to keep track of whether a `SymbolDef` has been
- // properly assigned before use:
- // - `push()` & `pop()` when visiting conditional branches
- // - backup & restore via `save_ids` when visiting out-of-order sections
- var safe_ids = Object.create(null);
- var suppressor = new TreeWalker(function(node) {
- if (!(node instanceof AST_Symbol)) return;
- var d = node.definition();
- if (!d) return;
- if (node instanceof AST_SymbolRef) d.references.push(node);
- d.fixed = false;
- });
- var in_loop = null;
- var loop_ids = Object.create(null);
- var tw = new TreeWalker(function(node, descend) {
- node._squeezed = false;
- node._optimized = false;
- if (reduce_vars) {
- if (node instanceof AST_Toplevel) node.globals.each(reset_def);
- if (node instanceof AST_Scope) node.variables.each(reset_def);
- if (node instanceof AST_SymbolRef) {
- var d = node.definition();
- d.references.push(node);
- if (d.references.length == 1
- && !d.fixed
- && d.orig[0] instanceof AST_SymbolDefun) {
- loop_ids[d.id] = in_loop;
- }
- var value;
- if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") {
- d.fixed = false;
- } else if (d.fixed) {
- value = node.fixed_value();
- if (value && ref_once(d)) {
- d.single_use = value instanceof AST_Lambda
- || d.scope === node.scope && value.is_constant_expression();
- } else {
- d.single_use = false;
- }
- if (is_modified(node, value, 0, is_immutable(value))) {
- if (d.single_use) {
- d.single_use = "m";
- } else {
- d.fixed = false;
- }
- }
- }
- mark_escaped(d, node.scope, node, value, 0);
- }
- if (node instanceof AST_SymbolCatch) {
- node.definition().fixed = false;
- }
- if (node instanceof AST_VarDef) {
- var d = node.name.definition();
- if (d.fixed === undefined || safe_to_assign(d, node.value)) {
- if (node.value) {
- d.fixed = function() {
- return node.value;
- };
- loop_ids[d.id] = in_loop;
- mark(d, false);
- descend();
- } else {
- d.fixed = null;
- }
- mark(d, true);
- return true;
- } else if (node.value) {
- d.fixed = false;
- }
- }
- if (node instanceof AST_Assign
- && node.operator == "="
- && node.left instanceof AST_SymbolRef) {
- var d = node.left.definition();
- if (safe_to_assign(d, node.right)
- || d.fixed === undefined && all(d.orig, function(sym) {
- return sym instanceof AST_SymbolVar;
- })) {
- d.references.push(node.left);
- d.fixed = function() {
- return node.right;
- };
- mark(d, false);
- node.right.walk(tw);
- mark(d, true);
- return true;
- }
- }
- if (node instanceof AST_Defun) {
- node.inlined = false;
- var d = node.name.definition();
- if (compressor.exposed(d) || safe_to_read(d)) {
- d.fixed = false;
- } else {
- d.fixed = node;
- d.single_use = ref_once(d);
- loop_ids[d.id] = in_loop;
- mark(d, true);
- }
- var save_ids = safe_ids;
- safe_ids = Object.create(null);
- descend();
- safe_ids = save_ids;
- return true;
- }
- if (node instanceof AST_Function) {
- node.inlined = false;
- push();
- var iife;
- if (!node.name
- && (iife = tw.parent()) instanceof AST_Call
- && iife.expression === node) {
- // Virtually turn IIFE parameters into variable definitions:
- // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
- // So existing transformation rules can work on them.
- node.argnames.forEach(function(arg, i) {
- var d = arg.definition();
- if (!node.uses_arguments && d.fixed === undefined) {
- d.fixed = function() {
- return iife.args[i] || make_node(AST_Undefined, iife);
- };
- loop_ids[d.id] = in_loop;
- mark(d, true);
- } else {
- d.fixed = false;
- }
- });
- }
- descend();
- pop();
- return true;
- }
- if (node instanceof AST_Accessor) {
- push();
- descend();
- pop();
- return true;
- }
- if (node instanceof AST_Binary && lazy_op(node.operator)) {
- node.left.walk(tw);
- push();
- node.right.walk(tw);
- pop();
- return true;
- }
- if (node instanceof AST_Conditional) {
- node.condition.walk(tw);
- push();
- node.consequent.walk(tw);
- pop();
- push();
- node.alternative.walk(tw);
- pop();
- return true;
- }
- if (node instanceof AST_If) {
- node.condition.walk(tw);
- push();
- node.body.walk(tw);
- pop();
- if (node.alternative) {
- push();
- node.alternative.walk(tw);
- pop();
- }
- return true;
- }
- if (node instanceof AST_Do) {
- var saved_loop = in_loop;
- in_loop = node;
- push();
- node.body.walk(tw);
- node.condition.walk(tw);
- pop();
- in_loop = saved_loop;
- return true;
- }
- if (node instanceof AST_While) {
- var saved_loop = in_loop;
- in_loop = node;
- push();
- node.condition.walk(tw);
- node.body.walk(tw);
- pop();
- in_loop = saved_loop;
- return true;
- }
- if (node instanceof AST_LabeledStatement) {
- push();
- node.body.walk(tw);
- pop();
- return true;
- }
- if (node instanceof AST_For) {
- if (node.init) node.init.walk(tw);
- var saved_loop = in_loop;
- in_loop = node;
- if (node.condition) {
- push();
- node.condition.walk(tw);
- pop();
- }
- push();
- node.body.walk(tw);
- pop();
- if (node.step) {
- push();
- node.step.walk(tw);
- pop();
- }
- in_loop = saved_loop;
- return true;
- }
- if (node instanceof AST_ForIn) {
- node.init.walk(suppressor);
- node.object.walk(tw);
- var saved_loop = in_loop;
- in_loop = node;
- push();
- node.body.walk(tw);
- pop();
- in_loop = saved_loop;
- return true;
- }
- if (node instanceof AST_Try) {
- push();
- walk_body(node, tw);
- pop();
- if (node.bcatch) {
- push();
- node.bcatch.walk(tw);
- pop();
- }
- if (node.bfinally) node.bfinally.walk(tw);
- return true;
- }
- if (node instanceof AST_SwitchBranch) {
- push();
- descend();
- pop();
- return true;
- }
+ (function(def){
+ def(AST_Node, noop);
+
+ function reset_def(compressor, def) {
+ def.direct_access = false;
+ def.escaped = false;
+ if (def.scope.uses_eval || def.scope.uses_with) {
+ def.fixed = false;
+ } else if (!compressor.exposed(def)) {
+ def.fixed = undefined;
+ } else {
+ def.fixed = false;
}
- });
- this.walk(tw);
+ def.references = [];
+ def.should_replace = undefined;
+ def.single_use = undefined;
+ }
+
+ function reset_variables(compressor, node) {
+ node.variables.each(function(def) {
+ reset_def(compressor, def);
+ });
+ }
- function mark(def, safe) {
- safe_ids[def.id] = safe;
+ function push(tw) {
+ tw.safe_ids = Object.create(tw.safe_ids);
}
- function safe_to_read(def) {
- if (safe_ids[def.id]) {
+ function pop(tw) {
+ tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
+ }
+
+ function mark(tw, def, safe) {
+ tw.safe_ids[def.id] = safe;
+ }
+
+ function safe_to_read(tw, def) {
+ if (tw.safe_ids[def.id]) {
if (def.fixed == null) {
var orig = def.orig[0];
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
@@ -574,9 +354,9 @@ merge(Compressor.prototype, {
return def.fixed instanceof AST_Defun;
}
- function safe_to_assign(def, value) {
- if (!HOP(safe_ids, def.id)) return false;
- if (!safe_to_read(def)) return false;
+ function safe_to_assign(tw, def, value) {
+ if (!HOP(tw.safe_ids, def.id)) return false;
+ if (!safe_to_read(tw, def)) return false;
if (def.fixed === false) return false;
if (def.fixed != null && (!value || def.references.length > 0)) return false;
return all(def.orig, function(sym) {
@@ -585,35 +365,12 @@ merge(Compressor.prototype, {
});
}
- function push() {
- safe_ids = Object.create(safe_ids);
- }
-
- function pop() {
- safe_ids = Object.getPrototypeOf(safe_ids);
- }
-
- function reset_def(def) {
- def.direct_access = false;
- def.escaped = false;
- if (def.scope.uses_eval || def.scope.uses_with) {
- def.fixed = false;
- } else if (!compressor.exposed(def)) {
- def.fixed = undefined;
- } else {
- def.fixed = false;
- }
- def.references = [];
- def.should_replace = undefined;
- def.single_use = undefined;
- }
-
- function ref_once(def) {
- return unused
+ function ref_once(tw, compressor, def) {
+ return compressor.option("unused")
&& !def.scope.uses_eval
&& !def.scope.uses_with
&& def.references.length == 1
- && loop_ids[def.id] === in_loop;
+ && tw.loop_ids[def.id] === tw.in_loop;
}
function is_immutable(value) {
@@ -642,7 +399,7 @@ merge(Compressor.prototype, {
return value instanceof AST_SymbolRef && value.fixed_value() || value;
}
- function is_modified(node, value, level, immutable) {
+ function is_modified(tw, node, value, level, immutable) {
var parent = tw.parent(level);
if (is_lhs(node, parent)
|| !immutable
@@ -652,16 +409,16 @@ merge(Compressor.prototype, {
|| !(parent instanceof AST_New) && value.contains_this())) {
return true;
} else if (parent instanceof AST_Array) {
- return is_modified(parent, parent, level + 1);
+ return is_modified(tw, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
- return is_modified(obj, obj, level + 2);
+ return is_modified(tw, obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
- return !immutable && is_modified(parent, read_property(value, parent.property), level + 1);
+ return !immutable && is_modified(tw, parent, read_property(value, parent.property), level + 1);
}
}
- function mark_escaped(d, scope, node, value, level) {
+ function mark_escaped(tw, d, scope, node, value, level) {
var parent = tw.parent(level);
if (value) {
if (value.is_constant()) return;
@@ -677,17 +434,278 @@ merge(Compressor.prototype, {
|| 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(d, scope, parent, parent, level + 1);
+ mark_escaped(tw, d, scope, parent, parent, level + 1);
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
var obj = tw.parent(level + 1);
- mark_escaped(d, scope, obj, obj, level + 2);
+ mark_escaped(tw, d, scope, obj, obj, level + 2);
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
value = read_property(value, parent.property);
- mark_escaped(d, scope, parent, value, level + 1);
+ mark_escaped(tw, d, scope, parent, value, level + 1);
if (value) return;
}
if (level == 0) d.direct_access = true;
}
+
+ var suppressor = new TreeWalker(function(node) {
+ if (!(node instanceof AST_Symbol)) return;
+ var d = node.definition();
+ if (!d) return;
+ if (node instanceof AST_SymbolRef) d.references.push(node);
+ d.fixed = false;
+ });
+ def(AST_Accessor, function(tw, descend) {
+ push(tw);
+ descend();
+ pop(tw);
+ return true;
+ });
+ def(AST_Assign, function(tw) {
+ var node = this;
+ if (node.operator != "=" || !(node.left instanceof AST_SymbolRef)) return;
+ var d = node.left.definition();
+ if (safe_to_assign(tw, d, node.right)
+ || d.fixed === undefined && all(d.orig, function(sym) {
+ return sym instanceof AST_SymbolVar;
+ })) {
+ d.references.push(node.left);
+ d.fixed = function() {
+ return node.right;
+ };
+ mark(tw, d, false);
+ node.right.walk(tw);
+ mark(tw, d, true);
+ return true;
+ }
+ });
+ def(AST_Binary, function(tw) {
+ if (!lazy_op(this.operator)) return;
+ this.left.walk(tw);
+ push(tw);
+ this.right.walk(tw);
+ pop(tw);
+ return true;
+ });
+ def(AST_Conditional, function(tw) {
+ this.condition.walk(tw);
+ push(tw);
+ this.consequent.walk(tw);
+ pop(tw);
+ push(tw);
+ this.alternative.walk(tw);
+ pop(tw);
+ return true;
+ });
+ def(AST_Defun, function(tw, descend, compressor) {
+ reset_variables(compressor, this);
+ this.inlined = false;
+ var d = this.name.definition();
+ if (compressor.exposed(d) || safe_to_read(tw, d)) {
+ d.fixed = false;
+ } else {
+ d.fixed = this;
+ d.single_use = ref_once(tw, compressor, d);
+ tw.loop_ids[d.id] = tw.in_loop;
+ mark(tw, d, true);
+ }
+ var save_ids = tw.safe_ids;
+ tw.safe_ids = Object.create(null);
+ descend();
+ tw.safe_ids = save_ids;
+ return true;
+ });
+ def(AST_Do, function(tw) {
+ var saved_loop = tw.in_loop;
+ tw.in_loop = this;
+ push(tw);
+ this.body.walk(tw);
+ this.condition.walk(tw);
+ pop(tw);
+ tw.in_loop = saved_loop;
+ return true;
+ });
+ def(AST_For, function(tw) {
+ if (this.init) this.init.walk(tw);
+ var saved_loop = tw.in_loop;
+ tw.in_loop = this;
+ if (this.condition) {
+ push(tw);
+ this.condition.walk(tw);
+ pop(tw);
+ }
+ push(tw);
+ this.body.walk(tw);
+ pop(tw);
+ if (this.step) {
+ push(tw);
+ this.step.walk(tw);
+ pop(tw);
+ }
+ tw.in_loop = saved_loop;
+ return true;
+ });
+ def(AST_ForIn, function(tw) {
+ this.init.walk(suppressor);
+ this.object.walk(tw);
+ var saved_loop = tw.in_loop;
+ tw.in_loop = this;
+ push(tw);
+ this.body.walk(tw);
+ pop(tw);
+ tw.in_loop = saved_loop;
+ return true;
+ });
+ def(AST_Function, function(tw, descend, compressor) {
+ var node = this;
+ reset_variables(compressor, node);
+ node.inlined = false;
+ push(tw);
+ var iife;
+ if (!node.name
+ && (iife = tw.parent()) instanceof AST_Call
+ && iife.expression === node) {
+ // Virtually turn IIFE parameters into variable definitions:
+ // (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
+ // So existing transformation rules can work on them.
+ node.argnames.forEach(function(arg, i) {
+ var d = arg.definition();
+ if (!node.uses_arguments && d.fixed === undefined) {
+ d.fixed = function() {
+ return iife.args[i] || make_node(AST_Undefined, iife);
+ };
+ tw.loop_ids[d.id] = tw.in_loop;
+ mark(tw, d, true);
+ } else {
+ d.fixed = false;
+ }
+ });
+ }
+ descend();
+ pop(tw);
+ return true;
+ });
+ def(AST_If, function(tw) {
+ this.condition.walk(tw);
+ push(tw);
+ this.body.walk(tw);
+ pop(tw);
+ if (this.alternative) {
+ push(tw);
+ this.alternative.walk(tw);
+ pop(tw);
+ }
+ return true;
+ });
+ def(AST_LabeledStatement, function(tw) {
+ push(tw);
+ this.body.walk(tw);
+ pop(tw);
+ return true;
+ });
+ def(AST_SwitchBranch, function(tw, descend) {
+ push(tw);
+ descend();
+ pop(tw);
+ return true;
+ });
+ def(AST_SymbolCatch, function() {
+ this.definition().fixed = false;
+ });
+ def(AST_SymbolRef, function(tw, descend, compressor) {
+ var d = this.definition();
+ d.references.push(this);
+ if (d.references.length == 1
+ && !d.fixed
+ && d.orig[0] instanceof AST_SymbolDefun) {
+ tw.loop_ids[d.id] = tw.in_loop;
+ }
+ var value;
+ if (d.fixed === undefined || !safe_to_read(tw, d) || d.single_use == "m") {
+ d.fixed = false;
+ } else if (d.fixed) {
+ value = this.fixed_value();
+ if (value && ref_once(tw, compressor, d)) {
+ d.single_use = value instanceof AST_Lambda
+ || d.scope === this.scope && value.is_constant_expression();
+ } else {
+ d.single_use = false;
+ }
+ if (is_modified(tw, this, value, 0, is_immutable(value))) {
+ if (d.single_use) {
+ d.single_use = "m";
+ } else {
+ d.fixed = false;
+ }
+ }
+ }
+ mark_escaped(tw, d, this.scope, this, value, 0);
+ });
+ def(AST_Toplevel, function(tw, descend, compressor) {
+ this.globals.each(function(def) {
+ reset_def(compressor, def);
+ });
+ reset_variables(compressor, this);
+ });
+ def(AST_Try, function(tw) {
+ push(tw);
+ walk_body(this, tw);
+ pop(tw);
+ if (this.bcatch) {
+ push(tw);
+ this.bcatch.walk(tw);
+ pop(tw);
+ }
+ if (this.bfinally) this.bfinally.walk(tw);
+ return true;
+ });
+ def(AST_VarDef, function(tw, descend) {
+ var node = this;
+ var d = node.name.definition();
+ if (d.fixed === undefined || safe_to_assign(tw, d, node.value)) {
+ if (node.value) {
+ d.fixed = function() {
+ return node.value;
+ };
+ tw.loop_ids[d.id] = tw.in_loop;
+ mark(tw, d, false);
+ descend();
+ } else {
+ d.fixed = null;
+ }
+ mark(tw, d, true);
+ return true;
+ } else if (node.value) {
+ d.fixed = false;
+ }
+ });
+ def(AST_While, function(tw) {
+ var saved_loop = tw.in_loop;
+ tw.in_loop = this;
+ push(tw);
+ this.condition.walk(tw);
+ this.body.walk(tw);
+ pop(tw);
+ tw.in_loop = saved_loop;
+ return true;
+ });
+ })(function(node, func){
+ node.DEFMETHOD("reduce_vars", func);
+ });
+
+ AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
+ var reduce_vars = compressor.option("reduce_vars");
+ var tw = new TreeWalker(function(node, descend) {
+ node._squeezed = false;
+ node._optimized = false;
+ if (reduce_vars) return node.reduce_vars(tw, descend, compressor);
+ });
+ // Stack of look-up tables to keep track of whether a `SymbolDef` has been
+ // properly assigned before use:
+ // - `push()` & `pop()` when visiting conditional branches
+ // - backup & restore via `save_ids` when visiting out-of-order sections
+ tw.safe_ids = Object.create(null);
+ tw.in_loop = null;
+ tw.loop_ids = Object.create(null);
+ this.walk(tw);
});
AST_Symbol.DEFMETHOD("fixed_value", function() {