};
}
-Compressor.prototype = new TreeTransformer;
-merge(Compressor.prototype, {
- option: function(key) { return this.options[key] },
- exposed: function(def) {
- if (def.exported) return true;
- if (def.undeclared) return true;
- if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
- var toplevel = this.toplevel;
- return !all(def.orig, function(sym) {
- return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
- });
- },
- compress: function(node) {
- node = node.resolve_defines(this);
- node.hoist_exports(this);
- if (this.option("expression")) {
- node.process_expression(true);
- }
- var merge_vars = this.options.merge_vars;
- var passes = +this.options.passes || 1;
- var min_count = 1 / 0;
- var stopping = false;
- var mangle = { ie: this.option("ie") };
- for (var pass = 0; pass < passes; pass++) {
- node.figure_out_scope(mangle);
- if (pass > 0 || this.option("reduce_vars"))
- node.reset_opt_flags(this);
- this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
- node = node.transform(this);
- if (passes > 1) {
- var count = 0;
- node.walk(new TreeWalker(function() {
- count++;
- }));
- AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
- pass: pass,
- min_count: min_count,
- count: count,
- });
- if (count < min_count) {
- min_count = count;
- stopping = false;
- } else if (stopping) {
- break;
- } else {
- stopping = true;
- }
+Compressor.prototype = new TreeTransformer(function(node, descend, in_list) {
+ if (node._squeezed) return node;
+ var is_scope = node instanceof AST_Scope;
+ if (is_scope) {
+ node.hoist_properties(this);
+ node.hoist_declarations(this);
+ node.process_boolean_returns(this);
+ }
+ // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
+ // would call AST_Node.transform() if a different instance of AST_Node is
+ // produced after OPT().
+ // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
+ // Migrate and defer all children's AST_Node.transform() to below, which
+ // will now happen after this parent AST_Node has been properly substituted
+ // thus gives a consistent AST snapshot.
+ descend(node, this);
+ // Existing code relies on how AST_Node.optimize() worked, and omitting the
+ // following replacement call would result in degraded efficiency of both
+ // output and performance.
+ descend(node, this);
+ var opt = node.optimize(this);
+ if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
+ opt.drop_unused(this);
+ if (opt.merge_variables(this)) opt.drop_unused(this);
+ descend(opt, this);
+ }
+ if (opt === node) opt._squeezed = true;
+ return opt;
+});
+Compressor.prototype.option = function(key) {
+ return this.options[key];
+};
+Compressor.prototype.exposed = function(def) {
+ if (def.exported) return true;
+ if (def.undeclared) return true;
+ if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
+ var toplevel = this.toplevel;
+ return !all(def.orig, function(sym) {
+ return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
+ });
+};
+Compressor.prototype.compress = function(node) {
+ node = node.resolve_defines(this);
+ node.hoist_exports(this);
+ if (this.option("expression")) {
+ node.process_expression(true);
+ }
+ var merge_vars = this.options.merge_vars;
+ var passes = +this.options.passes || 1;
+ var min_count = 1 / 0;
+ var stopping = false;
+ var mangle = { ie: this.option("ie") };
+ for (var pass = 0; pass < passes; pass++) {
+ node.figure_out_scope(mangle);
+ if (pass > 0 || this.option("reduce_vars"))
+ node.reset_opt_flags(this);
+ this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
+ node = node.transform(this);
+ if (passes > 1) {
+ var count = 0;
+ node.walk(new TreeWalker(function() {
+ count++;
+ }));
+ AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
+ pass: pass,
+ min_count: min_count,
+ count: count,
+ });
+ if (count < min_count) {
+ min_count = count;
+ stopping = false;
+ } else if (stopping) {
+ break;
+ } else {
+ stopping = true;
}
}
- if (this.option("expression")) {
- node.process_expression(false);
- }
- return node;
- },
- before: function(node, descend, in_list) {
- if (node._squeezed) return node;
- var is_scope = node instanceof AST_Scope;
- if (is_scope) {
- node.hoist_properties(this);
- node.hoist_declarations(this);
- node.process_boolean_returns(this);
- }
- // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
- // would call AST_Node.transform() if a different instance of AST_Node is
- // produced after OPT().
- // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
- // Migrate and defer all children's AST_Node.transform() to below, which
- // will now happen after this parent AST_Node has been properly substituted
- // thus gives a consistent AST snapshot.
- descend(node, this);
- // Existing code relies on how AST_Node.optimize() worked, and omitting the
- // following replacement call would result in degraded efficiency of both
- // output and performance.
- descend(node, this);
- var opt = node.optimize(this);
- if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
- opt.drop_unused(this);
- if (opt.merge_variables(this)) opt.drop_unused(this);
- descend(opt, this);
- }
- if (opt === node) opt._squeezed = true;
- return opt;
}
-});
+ if (this.option("expression")) {
+ node.process_expression(false);
+ }
+ return node;
+};
(function(OPT) {
OPT(AST_Node, function(self, compressor) {
function collapse(statements, compressor) {
if (scope.pinned()) return statements;
var args;
- var assignments = Object.create(null);
+ var assignments = new Dictionary();
var candidates = [];
- var declare_only = Object.create(null);
+ var declare_only = new Dictionary();
var force_single;
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) {
});
args = iife.args.slice();
var len = args.length;
- var names = Object.create(null);
+ var names = new Dictionary();
for (var i = fn.argnames.length; --i >= 0;) {
var sym = fn.argnames[i];
var arg = args[i];
candidates.length = 0;
break;
}
- if (sym.name in names) continue;
- names[sym.name] = true;
+ if (names.has(sym.name)) continue;
+ names.set(sym.name, true);
if (value) arg = !arg || is_undefined(arg) ? value : null;
if (!arg && !value) {
arg = make_node(AST_Undefined, sym).transform(compressor);
extract_candidates(lhs);
extract_candidates(expr.right);
if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
- assignments[lhs.name] = (assignments[lhs.name] || 0) + 1;
+ assignments.set(lhs.name, (assignments.get(lhs.name) || 0) + 1);
}
} else if (expr instanceof AST_Await) {
extract_candidates(expr.expression, unused);
candidates.push(hit_stack.slice());
}
} else {
- declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
+ declare_only.set(expr.name.name, (declare_only.get(expr.name.name) || 0) + 1);
}
}
if (expr.value) extract_candidates(expr.value);
}
function remaining_refs(def) {
- return def.references.length - def.replaced - (assignments[def.name] || 0);
+ return def.references.length - def.replaced - (assignments.get(def.name) || 0);
}
function get_lhs(expr) {
if (def.const_redefs) return;
if (!member(lhs, def.orig)) return;
if (scope.uses_arguments && is_funarg(def)) return;
- var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0);
+ var declared = def.orig.length - def.eliminated - (declare_only.get(def.name) || 0);
remaining = remaining_refs(def);
if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
if (!ref.fixed) return true;
if (hit_index <= end) return handle_custom_scan_order(node, tt);
hit = true;
if (node instanceof AST_VarDef) {
- declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1;
+ declare_only.set(node.name.name, (declare_only.get(node.name.name) || 0) + 1);
if (value_def) value_def.replaced++;
node = node.clone();
node.value = null;
}
function trim_assigns(name, value, exprs) {
- var names = Object.create(null);
- names[name.name] = true;
+ var names = new Dictionary();
+ names.set(name.name, true);
while (value instanceof AST_Assign && value.operator == "=") {
- if (value.left instanceof AST_SymbolRef) names[value.left.name] = true;
+ if (value.left instanceof AST_SymbolRef) names.set(value.left.name, true);
value = value.right;
}
if (!(value instanceof AST_Object)) return;
if (!(node.left instanceof AST_PropAccess)) return;
var sym = node.left.expression;
if (!(sym instanceof AST_SymbolRef)) return;
- if (!(sym.name in names)) return;
+ if (!names.has(sym.name)) return;
if (!node.right.is_constant_expression(scope)) return;
var prop = node.left.property;
if (prop instanceof AST_Node) {
}
return this;
});
- var nonsafe_props = makePredicate("__proto__ toString valueOf");
def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
if (compressor.option("unsafe")) {
var val = {};
key = key._eval(compressor, ignore_side_effects, cached, depth);
if (key === prop.key) return this;
}
- if (nonsafe_props[key]) return this;
+ switch (key) {
+ case "__proto__":
+ case "toString":
+ case "valueOf":
+ return this;
+ }
val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
if (val[key] === prop.value) return this;
}
var prop_keys, prop_map;
if (value instanceof AST_Object) {
prop_keys = [];
- prop_map = Object.create(null);
+ prop_map = new Dictionary();
value.properties.forEach(function(prop, index) {
if (prop instanceof AST_Spread) return prop_map = false;
var key = prop.key;
if (key instanceof AST_Node) {
prop_map = false;
} else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
- prop_map[key] = prop;
+ prop_map.set(key, prop);
}
prop_keys[index] = key;
});
value = false;
node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
}
- var can_drop = Object.create(null);
- var drop_keys = drop && Object.create(null);
+ var can_drop = new Dictionary();
+ var drop_keys = drop && new Dictionary();
var properties = [];
node.properties.map(function(prop) {
var key = prop.key;
if (key instanceof AST_Node) {
drop_keys = false;
} else {
- can_drop[key] = !(key in can_drop);
+ can_drop.set(key, !can_drop.has(key));
}
return key;
}).forEach(function(key, index) {
value = false;
trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
} else {
- drop = drop_keys && can_drop[key];
- var mapped = prop_map && prop_map[key];
+ drop = drop_keys && can_drop.get(key);
+ var mapped = prop_map && prop_map.get(key);
if (mapped) {
value = mapped.value;
if (value instanceof AST_Accessor) value = false;
trimmed = prop.value.transform(trimmer);
if (!trimmed) {
if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
- if (drop_keys && !(key in drop_keys)) {
+ if (drop_keys && !drop_keys.has(key)) {
if (mapped) {
- drop_keys[key] = mapped;
+ drop_keys.set(key, mapped);
if (value === null) {
- prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
+ prop_map.set(key, retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
key: mapped.key,
value: make_node(AST_Number, mapped, { value: 0 }),
- });
+ }));
}
} else {
- drop_keys[key] = true;
+ drop_keys.set(key, true);
}
}
} else if (drop_keys) {
- drop_keys[key] = false;
+ drop_keys.set(key, false);
}
if (value) mapped.value = value;
}
if (prop instanceof AST_Spread) return prop;
var key = prop_keys[index];
if (key instanceof AST_Node) return prop;
- if (key in drop_keys) {
- var mapped = drop_keys[key];
+ if (drop_keys.has(key)) {
+ var mapped = drop_keys.get(key);
if (!mapped) return prop;
- if (mapped === prop) return prop_map[key] || List.skip;
+ if (mapped === prop) return prop_map.get(key) || List.skip;
} else if (node.rest) {
return prop;
}
if (var_decl <= 1) hoist_vars = false;
}
if (!hoist_funs && !hoist_vars) return;
- var consts = Object.create(null);
+ var consts = new Dictionary();
var dirs = [];
var hoisted = [];
var vars = new Dictionary(), vars_found = 0;
if (!all(node.definitions, function(defn) {
var sym = defn.name;
return sym instanceof AST_SymbolVar
- && !consts[sym.name]
+ && !consts.has(sym.name)
&& self.find_variable(sym.name) === sym.definition();
})) return node;
node.definitions.forEach(function(def) {
}
if (node instanceof AST_Scope) return node;
if (node instanceof AST_SymbolConst) {
- consts[node.name] = true;
+ consts.set(node.name, true);
return node;
}
});
AST_BlockScope.DEFMETHOD("var_names", function() {
var var_names = this._var_names;
if (!var_names) {
- this._var_names = var_names = Object.create(null);
+ this._var_names = var_names = new Dictionary();
this.enclosed.forEach(function(def) {
- var_names[def.name] = true;
+ var_names.set(def.name, true);
});
this.variables.each(function(def, name) {
- var_names[name] = true;
+ var_names.set(name, true);
});
}
return var_names;
prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
var name = prefix;
for (var i = 0; !all(scopes, function(scope) {
- return !scope.var_names()[name];
+ return !scope.var_names().has(name);
}); i++) name = prefix + "$" + i;
var sym = make_node(type, orig, {
name: name,
var def = this.def_variable(sym);
scopes.forEach(function(scope) {
scope.enclosed.push(def);
- scope.var_names()[name] = true;
+ scope.var_names().set(name, true);
});
return sym;
});
var scope = def.scope.resolve();
for (var s = def.scope; s !== scope;) {
s = s.parent_scope;
- if (s.var_names()[def.name]) return true;
+ if (s.var_names().has(def.name)) return true;
}
}
def.scope = scope;
scope.variables.set(def.name, def);
scope.enclosed.push(def);
- scope.var_names()[def.name] = true;
+ scope.var_names().set(def.name, true);
}),
value: defn.value,
});
}
function var_exists(defined, name) {
- return defined[name] || identifier_atom[name] || scope.var_names()[name];
+ return defined.has(name) || identifier_atom[name] || scope.var_names().has(name);
}
- function can_inject_args(defined, used, safe_to_inject) {
+ function can_inject_args(defined, safe_to_inject) {
var abort = false;
fn.each_argname(function(arg) {
if (abort) return;
if (arg.__unused) return;
if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
- used[arg.name] = true;
+ arg_used.set(arg.name, true);
if (in_loop) in_loop.push(arg.definition());
});
return !abort;
}
- function can_inject_vars(defined, used, safe_to_inject) {
+ function can_inject_vars(defined, safe_to_inject) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (stat instanceof AST_LambdaDefinition) {
- if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
+ var name = stat.name;
+ if (!safe_to_inject) return false;
+ if (arg_used.has(name.name)) return false;
+ if (var_exists(defined, name.name)) return false;
if (!all(stat.enclosed, function(def) {
- return def.scope === stat || !defined[def.name];
+ return def.scope === stat || !defined.has(def.name);
})) return false;
- if (in_loop) in_loop.push(stat.name.definition());
+ if (in_loop) in_loop.push(name.definition());
continue;
}
if (!(stat instanceof AST_Var)) continue;
}
function can_inject_symbols() {
- var defined = Object.create(null);
+ var defined = new Dictionary();
var level = 0, child;
scope = current;
do {
if (scope.variables) scope.variables.each(function(def) {
- defined[def.name] = true;
+ defined.set(def.name, true);
});
child = scope;
scope = compressor.parent(level++);
var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
if (scope instanceof AST_Toplevel) {
if (compressor.toplevel.vars) {
- defined["arguments"] = true;
+ defined.set("arguments", true);
} else {
safe_to_inject = false;
}
}
+ arg_used = new Dictionary();
var inline = compressor.option("inline");
- arg_used = Object.create(defined);
- if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
- var used = Object.create(arg_used);
- if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
+ if (!can_inject_args(defined, inline >= 2 && safe_to_inject)) return false;
+ if (!can_inject_vars(defined, inline >= 3 && safe_to_inject)) return false;
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
}
function append_var(decls, expressions, name, value) {
var def = name.definition();
- if (!scope.var_names()[name.name]) {
- scope.var_names()[name.name] = true;
+ if (!scope.var_names().has(name.name)) {
+ scope.var_names().set(name.name, true);
decls.push(make_node(AST_VarDef, name, {
name: name,
value: null,
name = argname;
}
var value = self.args[i];
- if (name.__unused || scope.var_names()[name.name]) {
+ if (name.__unused || scope.var_names().has(name.name)) {
if (value) expressions.push(value);
} else {
var symbol = make_node(AST_SymbolVar, name, name);
scope.functions.set(def.name, def);
scope.variables.set(def.name, def);
scope.enclosed.push(def);
- scope.var_names()[def.name] = true;
+ scope.var_names().set(def.name, true);
args.push(stat);
}
continue;
var var_def = stat.definitions[j];
var name = flatten_var(var_def.name);
append_var(decl_var, expr_var, name, var_def.value);
- if (in_loop && !HOP(arg_used, name.name)) {
+ if (in_loop && !arg_used.has(name.name)) {
var def = fn.variables.get(name.name);
var sym = make_node(AST_SymbolRef, name, name);
def.references.push(sym);
}));
[].splice.apply(scope.body, args);
fn.enclosed.forEach(function(def) {
- if (scope.var_names()[def.name]) return;
+ if (scope.var_names().has(def.name)) return;
scope.enclosed.push(def);
- scope.var_names()[def.name] = true;
+ scope.var_names().set(def.name, true);
});
return expressions;
}
var scope = self.scope.resolve();
fixed.enclosed.forEach(function(def) {
if (fixed.variables.has(def.name)) return;
- if (scope.var_names()[def.name]) return;
+ if (scope.var_names().has(def.name)) return;
scope.enclosed.push(def);
- scope.var_names()[def.name] = true;
+ scope.var_names().set(def.name, true);
});
}
var value;