} while (sym = sym.parent_scope);
}
- function can_drop_symbol(tw, ref, keep_lambda) {
+ function can_drop_symbol(ref, keep_lambda) {
var orig = ref.definition().orig;
if (ref.in_arg && (orig[0] instanceof AST_SymbolFunarg || orig[1] instanceof AST_SymbolFunarg)) return false;
return all(orig, function(sym) {
var d = sym.definition();
d.assignments++;
if (!is_modified(compressor, tw, node, node.right, 0)
- && can_drop_symbol(tw, sym) && safe_to_assign(tw, d)) {
+ && can_drop_symbol(sym) && safe_to_assign(tw, d)) {
push_ref(d, sym);
mark(tw, d);
tw.loop_ids[d.id] = tw.in_loop;
if (node instanceof AST_DWLoop) return true;
if (node instanceof AST_LoopControl) return true;
if (node instanceof AST_SymbolRef) {
- if (node.is_declared(compressor) ? node.fixed_value() || all(node.definition().orig, function(sym) {
- return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
- }) : parent instanceof AST_Assign && parent.operator == "=" && parent.left === node) return false;
+ if (node.is_declared(compressor)) {
+ if (node.fixed_value() || can_drop_symbol(node)) return false;
+ } else if (parent instanceof AST_Assign && parent.operator == "=" && parent.left === node) {
+ return false;
+ }
if (!replace_all) return true;
scan_rhs = false;
return false;
}
if (side_effects && may_modify(node)) return true;
var def = node.definition();
- return (in_try || def.scope.resolve() !== scope) && !all(def.orig, function(sym) {
- return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
- });
+ return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
}
if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
if (node instanceof AST_VarDef) {
if ((in_try || !lhs_local) && node.name instanceof AST_Destructured) return true;
- return node.value && node.name.match_symbol(function(node) {
+ return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
return node instanceof AST_SymbolDeclaration
&& (lvalues.has(node.name) || side_effects && may_modify(node));
});
});
def(AST_SymbolDeclaration, return_false);
def(AST_SymbolRef, function(compressor) {
- return !this.is_declared(compressor) || !can_drop_symbol(compressor, this);
+ return !this.is_declared(compressor) || !can_drop_symbol(this);
});
def(AST_This, return_false);
def(AST_Try, function(compressor) {
var self = this;
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
- var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(tw, node, props) {
+ var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
var sym, nested = false;
if (node instanceof AST_Assign) {
if (node.write_only || node.operator == "=") sym = node.left;
}
if (!(sym instanceof AST_SymbolRef)) return;
if (compressor.exposed(sym.definition())) return;
- if (!can_drop_symbol(tw, sym, nested)) return;
+ if (!can_drop_symbol(sym, nested)) return;
return sym;
};
var assign_in_use = Object.create(null);
var redef = def.redefined();
if (redef) var_defs_by_id.add(redef.id, defn);
}
- if ((!drop_vars || (node instanceof AST_Const ? def.redefined() : def.const_redefs))
- && !(def.id in in_use_ids)) {
+ if (!(def.id in in_use_ids) && (!drop_vars
+ || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
+ || !(node instanceof AST_Var || is_safe_lexical(def)))) {
in_use_ids[def.id] = true;
in_use.push(def);
}
var tt = new TreeTransformer(function(node, descend, in_list) {
var parent = tt.parent();
if (drop_vars) {
- var props = [], sym = assign_as_unused(tt, node, props);
+ var props = [], sym = assign_as_unused(node, props);
if (sym) {
var def = sym.definition();
var in_use = def.id in in_use_ids;
// into the next one, or next statement.
var side_effects = [];
var duplicated = 0;
+ var is_var = node instanceof AST_Var;
node.definitions.forEach(function(def) {
if (def.value) def.value = def.value.transform(tt);
if (def.name instanceof AST_Destructured) {
return node;
}
if (node instanceof AST_SymbolDeclaration) {
- return !drop_vars || node.definition().id in in_use_ids || is_catch(node) ? node : null;
+ if (!drop_vars) return node;
+ if (node.definition().id in in_use_ids) return node;
+ if (is_catch(node)) return node;
+ if (is_var && !can_drop_symbol(node)) return node;
+ return null;
}
});
var name = def.name.transform(trimmer);
return;
}
var sym = def.name.definition();
- if (!drop_vars || sym.id in in_use_ids) {
+ var drop_sym = !is_var || can_drop_symbol(def.name);
+ if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
if (def.value && indexOf_assign(sym, def) < 0) {
var write_only = def.value.write_only;
var value = def.value.drop_side_effect_free(compressor);
}
var old_def, var_defs = var_defs_by_id.get(sym.id);
if (!def.value && !(node instanceof AST_Let)) {
- if (var_defs.length > 1) {
+ if (drop_sym && var_defs.length > 1) {
AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
remove(var_defs, def);
sym.eliminated++;
});
body.push(defun);
} else {
- if (var_defs.length > 1 && sym.orig.indexOf(def.name) > sym.eliminated) {
+ if (drop_sym && var_defs.length > 1 && sym.orig.indexOf(def.name) > sym.eliminated) {
remove(var_defs, def);
duplicated++;
}
var def = node.expression.definition();
if (def.scope === self) assignments.add(def.id, node);
}
- var node_def, props = [], sym = assign_as_unused(tw, node, props);
+ var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
props.forEach(function(prop) {
prop.walk(tw);
&& all(def.references, function(ref) {
return ref.fixed_value() === right;
})
- && all(def.orig, function(sym) {
- return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
- });
+ && can_drop_symbol(sym);
}
});
return make_sequence(this, [ expression, property ]);
});
def(AST_SymbolRef, function(compressor) {
- return this.is_declared(compressor) && can_drop_symbol(compressor, this) ? null : this;
+ return this.is_declared(compressor) && can_drop_symbol(this) ? null : this;
});
def(AST_This, return_null);
def(AST_Unary, function(compressor, first_in_statement) {
this.write_only = !exp.has_side_effects(compressor);
return this;
}
- if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(compressor, exp)) {
- return null;
- }
+ if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp)) return null;
var node = exp.drop_side_effect_free(compressor, first_in_statement);
if (first_in_statement && node && is_iife_call(node)) {
if (node === exp && this.operator == "!") return this;
return make_sequence(this, assignments);
});
+ function is_safe_lexical(def) {
+ return def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
+ }
+
function may_overlap(compressor, def) {
if (compressor.exposed(def)) return true;
var scope = def.scope.resolve();
return compressor.option("varify") && all(self.definitions, function(defn) {
return !defn.name.match_symbol(function(node) {
if (node instanceof AST_SymbolDeclaration) {
- return !node.fixed_value() || may_overlap(compressor, node.definition());
+ var def = node.definition();
+ return !node.fixed_value() || !is_safe_lexical(def) || may_overlap(compressor, def);
}
}, true);
}) ? to_var(self) : self;
// typeof always returns a non-empty string, thus it's
// always true in booleans
AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
- return (exp instanceof AST_SymbolRef && all(exp.definition().orig, function(sym) {
- return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
- }) ? make_node(AST_True, self) : make_sequence(self, [
- exp,
- make_node(AST_True, self)
- ])).optimize(compressor);
+ var exprs = [ make_node(AST_True, self) ];
+ if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp))) exprs.unshift(exp);
+ return make_sequence(self, exprs).optimize(compressor);
}
}
if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);