improve handling of lexical scope (#4979)
This commit is contained in:
@@ -449,6 +449,13 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function has_escaped(d, scope, node, parent) {
|
||||||
|
if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
|
||||||
|
if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
|
||||||
|
if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
|
||||||
|
if (parent instanceof AST_VarDef) return parent.value === node;
|
||||||
|
}
|
||||||
|
|
||||||
var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
|
var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
|
||||||
(function(def) {
|
(function(def) {
|
||||||
def(AST_Node, noop);
|
def(AST_Node, noop);
|
||||||
@@ -594,7 +601,7 @@ merge(Compressor.prototype, {
|
|||||||
var safe = tw.safe_ids[def.id];
|
var safe = tw.safe_ids[def.id];
|
||||||
if (!HOP(tw.safe_ids, def.id)) {
|
if (!HOP(tw.safe_ids, def.id)) {
|
||||||
if (!safe) return false;
|
if (!safe) return false;
|
||||||
if (safe.read && def.scope !== tw.find_parent(AST_Scope)) return false;
|
if (safe.read && def.scope.resolve() !== tw.find_parent(AST_Scope)) return false;
|
||||||
safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
|
safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
|
||||||
}
|
}
|
||||||
if (def.fixed != null && safe.read) {
|
if (def.fixed != null && safe.read) {
|
||||||
@@ -630,13 +637,6 @@ merge(Compressor.prototype, {
|
|||||||
return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
|
return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
function has_escaped(d, node, parent) {
|
|
||||||
if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
|
|
||||||
if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
|
|
||||||
if (parent instanceof AST_Exit) return parent.value === node && node.scope !== d.scope;
|
|
||||||
if (parent instanceof AST_VarDef) return parent.value === node;
|
|
||||||
}
|
|
||||||
|
|
||||||
function value_in_use(node, parent) {
|
function value_in_use(node, parent) {
|
||||||
if (parent instanceof AST_Array) return true;
|
if (parent instanceof AST_Array) return true;
|
||||||
if (parent instanceof AST_Binary) return lazy_op[parent.operator];
|
if (parent instanceof AST_Binary) return lazy_op[parent.operator];
|
||||||
@@ -648,11 +648,11 @@ merge(Compressor.prototype, {
|
|||||||
function mark_escaped(tw, d, scope, node, value, level, depth) {
|
function mark_escaped(tw, d, scope, node, value, level, depth) {
|
||||||
var parent = tw.parent(level);
|
var parent = tw.parent(level);
|
||||||
if (value && value.is_constant()) return;
|
if (value && value.is_constant()) return;
|
||||||
if (has_escaped(d, node, parent)) {
|
if (has_escaped(d, scope, node, parent)) {
|
||||||
d.escaped.push(parent);
|
d.escaped.push(parent);
|
||||||
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
|
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
|
||||||
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
|
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
|
||||||
if (d.scope !== scope) d.escaped.cross_scope = true;
|
if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
|
||||||
return;
|
return;
|
||||||
} else if (value_in_use(node, parent)) {
|
} else if (value_in_use(node, parent)) {
|
||||||
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
|
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
|
||||||
@@ -1212,7 +1212,7 @@ merge(Compressor.prototype, {
|
|||||||
var recursive = recursive_ref(tw, d);
|
var recursive = recursive_ref(tw, d);
|
||||||
if (recursive) recursive.enclosed.forEach(function(def) {
|
if (recursive) recursive.enclosed.forEach(function(def) {
|
||||||
if (d === def) return;
|
if (d === def) return;
|
||||||
if (def.scope === recursive) return;
|
if (def.scope.resolve() === recursive) return;
|
||||||
var assigns = def.fixed && def.fixed.assigns;
|
var assigns = def.fixed && def.fixed.assigns;
|
||||||
if (!assigns) return;
|
if (!assigns) return;
|
||||||
if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
|
if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
|
||||||
@@ -1607,11 +1607,12 @@ merge(Compressor.prototype, {
|
|||||||
function safe_to_trim(stat) {
|
function safe_to_trim(stat) {
|
||||||
if (stat instanceof AST_LambdaDefinition) {
|
if (stat instanceof AST_LambdaDefinition) {
|
||||||
var def = stat.name.definition();
|
var def = stat.name.definition();
|
||||||
return def.scope === stat.name.scope || all(def.references, function(ref) {
|
var scope = stat.name.scope;
|
||||||
var scope = ref.scope;
|
return def.scope === scope || all(def.references, function(ref) {
|
||||||
|
var s = ref.scope;
|
||||||
do {
|
do {
|
||||||
if (scope === stat.name.scope) return true;
|
if (s === scope) return true;
|
||||||
} while (scope = scope.parent_scope);
|
} while (s = s.parent_scope);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return !is_lexical_definition(stat);
|
return !is_lexical_definition(stat);
|
||||||
@@ -2791,7 +2792,9 @@ merge(Compressor.prototype, {
|
|||||||
if (!value) {
|
if (!value) {
|
||||||
value = node;
|
value = node;
|
||||||
var def = node.definition();
|
var def = node.definition();
|
||||||
if (!def.undeclared && (def.assignments || !def.escaped || def.escaped.cross_scope)) {
|
if (!def.undeclared
|
||||||
|
&& (def.assignments || !def.escaped || def.escaped.cross_scope)
|
||||||
|
&& (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
|
||||||
well_defined = false;
|
well_defined = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2880,7 +2883,7 @@ merge(Compressor.prototype, {
|
|||||||
function is_lhs_local(lhs) {
|
function is_lhs_local(lhs) {
|
||||||
var sym = root_expr(lhs);
|
var sym = root_expr(lhs);
|
||||||
return sym instanceof AST_SymbolRef
|
return sym instanceof AST_SymbolRef
|
||||||
&& sym.definition().scope === scope
|
&& sym.definition().scope.resolve() === scope
|
||||||
&& !(in_loop
|
&& !(in_loop
|
||||||
&& (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
|
&& (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
|
||||||
|| candidate instanceof AST_Unary
|
|| candidate instanceof AST_Unary
|
||||||
@@ -2921,7 +2924,7 @@ merge(Compressor.prototype, {
|
|||||||
function may_modify(sym) {
|
function may_modify(sym) {
|
||||||
var def = sym.definition();
|
var def = sym.definition();
|
||||||
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
|
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
|
||||||
if (def.scope !== scope) return true;
|
if (def.scope.resolve() !== scope) return true;
|
||||||
if (modify_toplevel && compressor.exposed(def)) return true;
|
if (modify_toplevel && compressor.exposed(def)) return true;
|
||||||
return !all(def.references, function(ref) {
|
return !all(def.references, function(ref) {
|
||||||
return ref.scope.resolve() === scope;
|
return ref.scope.resolve() === scope;
|
||||||
@@ -2935,7 +2938,7 @@ merge(Compressor.prototype, {
|
|||||||
if (lhs) {
|
if (lhs) {
|
||||||
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
|
if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
|
||||||
if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
|
if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
|
||||||
if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
|
if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -3818,7 +3821,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
def(AST_ObjectIdentity, function(compressor, force) {
|
def(AST_ObjectIdentity, function(compressor, force) {
|
||||||
return is_strict(compressor, force) && !this.scope.new;
|
return is_strict(compressor, force) && !this.scope.resolve().new;
|
||||||
});
|
});
|
||||||
def(AST_Sequence, function(compressor) {
|
def(AST_Sequence, function(compressor) {
|
||||||
return this.tail_node()._dot_throw(compressor);
|
return this.tail_node()._dot_throw(compressor);
|
||||||
@@ -5021,7 +5024,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!(lhs instanceof AST_PropAccess)) return true;
|
if (!(lhs instanceof AST_PropAccess)) return true;
|
||||||
var node = lhs.expression;
|
var node = lhs.expression;
|
||||||
return !(node instanceof AST_ObjectIdentity)
|
return !(node instanceof AST_ObjectIdentity)
|
||||||
|| !node.scope.new
|
|| !node.scope.resolve().new
|
||||||
|| lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
|
|| lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
|
||||||
|| this.right.has_side_effects(compressor);
|
|| this.right.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
@@ -6773,7 +6776,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function track_assigns(def, node) {
|
function track_assigns(def, node) {
|
||||||
if (def.scope !== self) return false;
|
if (def.scope.resolve() !== self) return false;
|
||||||
if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
|
if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
|
||||||
return assign_in_use[def.id] !== false;
|
return assign_in_use[def.id] !== false;
|
||||||
}
|
}
|
||||||
@@ -6833,14 +6836,15 @@ merge(Compressor.prototype, {
|
|||||||
function scan_ref_scoped(node, descend, init) {
|
function scan_ref_scoped(node, descend, init) {
|
||||||
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
|
if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
|
||||||
var def = node.left.definition();
|
var def = node.left.definition();
|
||||||
if (def.scope === self) assignments.add(def.id, node);
|
if (def.scope.resolve() === self) assignments.add(def.id, node);
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
|
if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
|
||||||
var def = node.expression.definition();
|
var def = node.expression.definition();
|
||||||
if (def.scope === self) assignments.add(def.id, node);
|
if (def.scope.resolve() === self) assignments.add(def.id, node);
|
||||||
}
|
}
|
||||||
var node_def, props = [], sym = assign_as_unused(node, props);
|
var node_def, props = [], sym = assign_as_unused(node, props);
|
||||||
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())
|
if (sym && ((node_def = sym.definition()).scope.resolve() === self
|
||||||
|
|| self.variables.get(sym.name) === node_def)
|
||||||
&& !(is_arguments(node_def) && !all(self.argnames, function(argname) {
|
&& !(is_arguments(node_def) && !all(self.argnames, function(argname) {
|
||||||
return !argname.match_symbol(function(node) {
|
return !argname.match_symbol(function(node) {
|
||||||
if (node instanceof AST_SymbolFunarg) {
|
if (node instanceof AST_SymbolFunarg) {
|
||||||
@@ -6861,7 +6865,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
if (node instanceof AST_Assign) {
|
if (node instanceof AST_Assign) {
|
||||||
var right = get_rhs(node), shared = false;
|
var right = get_rhs(node), shared = false;
|
||||||
if (init && node.write_only === true && node_def.scope === self && !right.has_side_effects(compressor)) {
|
if (init && node.write_only === true && !right.has_side_effects(compressor)) {
|
||||||
initializations.add(node_def.id, right);
|
initializations.add(node_def.id, right);
|
||||||
} else {
|
} else {
|
||||||
right.walk(tw);
|
right.walk(tw);
|
||||||
@@ -6892,7 +6896,7 @@ merge(Compressor.prototype, {
|
|||||||
var sym = get_init_symbol(node);
|
var sym = get_init_symbol(node);
|
||||||
if (!sym) return;
|
if (!sym) return;
|
||||||
var def = sym.definition();
|
var def = sym.definition();
|
||||||
if (def.scope !== self) {
|
if (def.scope.resolve() !== self) {
|
||||||
var d = find_variable(sym.name);
|
var d = find_variable(sym.name);
|
||||||
if (d === def || d && d.redefined() === def) return;
|
if (d === def || d && d.redefined() === def) return;
|
||||||
}
|
}
|
||||||
@@ -7324,33 +7328,32 @@ merge(Compressor.prototype, {
|
|||||||
if (node instanceof AST_Assign) {
|
if (node instanceof AST_Assign) {
|
||||||
if (node.operator != "=") return;
|
if (node.operator != "=") return;
|
||||||
if (!node.write_only) return;
|
if (!node.write_only) return;
|
||||||
if (node.left.scope !== self) return;
|
|
||||||
if (!can_hoist(node.left, node.right, 1)) return;
|
if (!can_hoist(node.left, node.right, 1)) return;
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
var defs = new Dictionary();
|
var defs = new Dictionary();
|
||||||
var assignments = [];
|
var assignments = [];
|
||||||
var decls = [];
|
var decls = [];
|
||||||
node.right.properties.forEach(function(prop) {
|
node.right.properties.forEach(function(prop) {
|
||||||
var decl = make_sym(node.left, prop.key);
|
var decl = make_sym(AST_SymbolVar, node.left, prop.key);
|
||||||
decls.push(make_node(AST_VarDef, node, {
|
decls.push(make_node(AST_VarDef, node, {
|
||||||
name: decl,
|
name: decl,
|
||||||
value: null
|
value: null,
|
||||||
}));
|
}));
|
||||||
var sym = make_node(AST_SymbolRef, node, {
|
var sym = make_node(AST_SymbolRef, node, {
|
||||||
name: decl.name,
|
name: decl.name,
|
||||||
scope: self,
|
scope: self,
|
||||||
thedef: decl.definition()
|
thedef: decl.definition(),
|
||||||
});
|
});
|
||||||
sym.reference();
|
sym.reference();
|
||||||
assignments.push(make_node(AST_Assign, node, {
|
assignments.push(make_node(AST_Assign, node, {
|
||||||
operator: "=",
|
operator: "=",
|
||||||
left: sym,
|
left: sym,
|
||||||
right: prop.value
|
right: prop.value,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
defs_by_id[node.left.definition().id] = defs;
|
defs_by_id[node.left.definition().id] = defs;
|
||||||
self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
|
self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
|
||||||
definitions: decls
|
definitions: decls,
|
||||||
}));
|
}));
|
||||||
return make_sequence(node, assignments);
|
return make_sequence(node, assignments);
|
||||||
}
|
}
|
||||||
@@ -7362,16 +7365,16 @@ merge(Compressor.prototype, {
|
|||||||
var var_defs = [];
|
var var_defs = [];
|
||||||
node.value.properties.forEach(function(prop) {
|
node.value.properties.forEach(function(prop) {
|
||||||
var_defs.push(make_node(AST_VarDef, node, {
|
var_defs.push(make_node(AST_VarDef, node, {
|
||||||
name: make_sym(node.name, prop.key),
|
name: make_sym(node.name.CTOR, node.name, prop.key),
|
||||||
value: prop.value
|
value: prop.value,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
defs_by_id[node.name.definition().id] = defs;
|
defs_by_id[node.name.definition().id] = defs;
|
||||||
return List.splice(var_defs);
|
return List.splice(var_defs);
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_sym(sym, key) {
|
function make_sym(type, sym, key) {
|
||||||
var new_var = self.make_var(AST_SymbolVar, sym, sym.name + "_" + key);
|
var new_var = self.make_var(type, sym, sym.name + "_" + key);
|
||||||
defs.set(key, new_var.definition());
|
defs.set(key, new_var.definition());
|
||||||
return new_var;
|
return new_var;
|
||||||
}
|
}
|
||||||
@@ -7386,7 +7389,7 @@ merge(Compressor.prototype, {
|
|||||||
var sym = make_node(AST_SymbolRef, node, {
|
var sym = make_node(AST_SymbolRef, node, {
|
||||||
name: def.name,
|
name: def.name,
|
||||||
scope: node.expression.scope,
|
scope: node.expression.scope,
|
||||||
thedef: def
|
thedef: def,
|
||||||
});
|
});
|
||||||
sym.reference();
|
sym.reference();
|
||||||
return sym;
|
return sym;
|
||||||
@@ -7401,9 +7404,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!(sym instanceof AST_SymbolRef)) return;
|
if (!(sym instanceof AST_SymbolRef)) return;
|
||||||
if (!(sym.definition().id in defs_by_id)) return;
|
if (!(sym.definition().id in defs_by_id)) return;
|
||||||
var opt = node.clone();
|
var opt = node.clone();
|
||||||
opt[prop] = make_node(AST_Object, sym, {
|
opt[prop] = make_node(AST_Object, sym, { properties: [] });
|
||||||
properties: []
|
|
||||||
});
|
|
||||||
return opt;
|
return opt;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -7423,8 +7424,7 @@ merge(Compressor.prototype, {
|
|||||||
&& all(right.properties, can_hoist_property)
|
&& all(right.properties, can_hoist_property)
|
||||||
&& all(def.references, function(ref) {
|
&& all(def.references, function(ref) {
|
||||||
return ref.fixed_value() === right;
|
return ref.fixed_value() === right;
|
||||||
})
|
});
|
||||||
&& can_drop_symbol(sym);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -727,8 +727,7 @@ side_effects_cascade_1: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
function f(a, b) {
|
function f(a, b) {
|
||||||
(a -= 42) < 0 && (a = 0),
|
b.a = a = (a -= 42) < 0 ? 0 : a;
|
||||||
b.a = a;
|
|
||||||
}
|
}
|
||||||
var m = {}, n = {};
|
var m = {}, n = {};
|
||||||
f(13, m),
|
f(13, m),
|
||||||
|
|||||||
Reference in New Issue
Block a user