improve reset_opt_flags() (#2610)
This commit is contained in:
618
lib/compress.js
618
lib/compress.js
@@ -293,14 +293,12 @@ merge(Compressor.prototype, {
|
|||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
node.body[index] = node.body[index].transform(tt);
|
node.body[index] = node.body[index].transform(tt);
|
||||||
}
|
}
|
||||||
}
|
} else if (node instanceof AST_If) {
|
||||||
if (node instanceof AST_If) {
|
|
||||||
node.body = node.body.transform(tt);
|
node.body = node.body.transform(tt);
|
||||||
if (node.alternative) {
|
if (node.alternative) {
|
||||||
node.alternative = node.alternative.transform(tt);
|
node.alternative = node.alternative.transform(tt);
|
||||||
}
|
}
|
||||||
}
|
} else if (node instanceof AST_With) {
|
||||||
if (node instanceof AST_With) {
|
|
||||||
node.body = node.body.transform(tt);
|
node.body = node.body.transform(tt);
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
@@ -308,292 +306,10 @@ merge(Compressor.prototype, {
|
|||||||
self.transform(tt);
|
self.transform(tt);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
|
(function(def){
|
||||||
var reduce_vars = compressor.option("reduce_vars");
|
def(AST_Node, noop);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.walk(tw);
|
|
||||||
|
|
||||||
function mark(def, safe) {
|
function reset_def(compressor, def) {
|
||||||
safe_ids[def.id] = safe;
|
|
||||||
}
|
|
||||||
|
|
||||||
function safe_to_read(def) {
|
|
||||||
if (safe_ids[def.id]) {
|
|
||||||
if (def.fixed == null) {
|
|
||||||
var orig = def.orig[0];
|
|
||||||
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
|
|
||||||
def.fixed = make_node(AST_Undefined, orig);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
if (def.fixed === false) return false;
|
|
||||||
if (def.fixed != null && (!value || def.references.length > 0)) return false;
|
|
||||||
return all(def.orig, function(sym) {
|
|
||||||
return !(sym instanceof AST_SymbolDefun
|
|
||||||
|| sym instanceof AST_SymbolLambda);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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.direct_access = false;
|
||||||
def.escaped = false;
|
def.escaped = false;
|
||||||
if (def.scope.uses_eval || def.scope.uses_with) {
|
if (def.scope.uses_eval || def.scope.uses_with) {
|
||||||
@@ -608,12 +324,53 @@ merge(Compressor.prototype, {
|
|||||||
def.single_use = undefined;
|
def.single_use = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ref_once(def) {
|
function reset_variables(compressor, node) {
|
||||||
return unused
|
node.variables.each(function(def) {
|
||||||
|
reset_def(compressor, def);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function push(tw) {
|
||||||
|
tw.safe_ids = Object.create(tw.safe_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
def.fixed = make_node(AST_Undefined, orig);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return def.fixed instanceof AST_Defun;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return !(sym instanceof AST_SymbolDefun
|
||||||
|
|| sym instanceof AST_SymbolLambda);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ref_once(tw, compressor, def) {
|
||||||
|
return compressor.option("unused")
|
||||||
&& !def.scope.uses_eval
|
&& !def.scope.uses_eval
|
||||||
&& !def.scope.uses_with
|
&& !def.scope.uses_with
|
||||||
&& def.references.length == 1
|
&& def.references.length == 1
|
||||||
&& loop_ids[def.id] === in_loop;
|
&& tw.loop_ids[def.id] === tw.in_loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_immutable(value) {
|
function is_immutable(value) {
|
||||||
@@ -642,7 +399,7 @@ merge(Compressor.prototype, {
|
|||||||
return value instanceof AST_SymbolRef && value.fixed_value() || value;
|
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);
|
var parent = tw.parent(level);
|
||||||
if (is_lhs(node, parent)
|
if (is_lhs(node, parent)
|
||||||
|| !immutable
|
|| !immutable
|
||||||
@@ -652,16 +409,16 @@ merge(Compressor.prototype, {
|
|||||||
|| !(parent instanceof AST_New) && value.contains_this())) {
|
|| !(parent instanceof AST_New) && value.contains_this())) {
|
||||||
return true;
|
return true;
|
||||||
} else if (parent instanceof AST_Array) {
|
} 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) {
|
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
||||||
var obj = tw.parent(level + 1);
|
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) {
|
} 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);
|
var parent = tw.parent(level);
|
||||||
if (value) {
|
if (value) {
|
||||||
if (value.is_constant()) return;
|
if (value.is_constant()) return;
|
||||||
@@ -677,17 +434,278 @@ merge(Compressor.prototype, {
|
|||||||
|| parent instanceof AST_Binary && lazy_op(parent.operator)
|
|| parent instanceof AST_Binary && lazy_op(parent.operator)
|
||||||
|| parent instanceof AST_Conditional && node !== parent.condition
|
|| parent instanceof AST_Conditional && node !== parent.condition
|
||||||
|| parent instanceof AST_Sequence && node === parent.tail_node()) {
|
|| 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) {
|
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
||||||
var obj = tw.parent(level + 1);
|
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) {
|
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
|
||||||
value = read_property(value, parent.property);
|
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 (value) return;
|
||||||
}
|
}
|
||||||
if (level == 0) d.direct_access = true;
|
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() {
|
AST_Symbol.DEFMETHOD("fixed_value", function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user