});
}
}
+ if (this.options["inline"] === true) this.options["inline"] = 3;
var pure_funcs = this.options["pure_funcs"];
if (typeof pure_funcs == "function") {
this.pure_funcs = pure_funcs;
if (compressor.option("inline")
&& !fn.uses_arguments
&& !fn.uses_eval
- && fn.body.length == 1
+ && (value = can_flatten_body(stat))
&& (exp === fn ? !fn.name
: compressor.option("unused")
&& (def = exp.definition()).references.length == 1
&& fn.is_constant_expression(exp.scope))
&& !self.pure
&& !fn.contains_this()
- && can_flatten_args(fn)
- && (value = flatten_body(stat))) {
- var expressions = flatten_args(fn);
- expressions.push(value.clone(true));
- return make_sequence(self, expressions).optimize(compressor);
+ && can_inject_symbols()) {
+ return make_sequence(self, flatten_fn()).optimize(compressor);
}
if (compressor.option("side_effects") && all(fn.body, is_empty)) {
var args = self.args.concat(make_node(AST_Undefined, self));
}
return self;
- function can_flatten_args(fn) {
- var catches = Object.create(null);
- do {
- scope = compressor.parent(++level);
- if (scope instanceof AST_Catch) {
- catches[scope.argname.name] = true;
- } else if (scope instanceof AST_IterationStatement) {
- in_loop = [];
- } else if (scope instanceof AST_SymbolRef) {
- if (scope.fixed_value() instanceof AST_Scope) return false;
+ function return_value(stat) {
+ if (!stat) return make_node(AST_Undefined, self);
+ if (stat instanceof AST_Return) {
+ if (!stat.value) return make_node(AST_Undefined, self);
+ return stat.value.clone(true);
+ }
+ if (stat instanceof AST_SimpleStatement) {
+ return make_node(AST_UnaryPrefix, stat, {
+ operator: "void",
+ expression: stat.body.clone(true)
+ });
+ }
+ }
+
+ function can_flatten_body(stat) {
+ var len = fn.body.length;
+ if (compressor.option("inline") < 3) {
+ return len == 1 && return_value(stat);
+ }
+ stat = null;
+ for (var i = 0; i < len; i++) {
+ var line = fn.body[i];
+ if (line instanceof AST_Definitions) {
+ if (stat && !all(line.definitions, function(var_def) {
+ return !var_def.value;
+ })) {
+ return false;
+ }
+ } else if (stat) {
+ return false;
+ } else {
+ stat = line;
}
- } while (!(scope instanceof AST_Scope));
- var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel);
+ }
+ return return_value(stat);
+ }
+
+ function can_inject_args(catches, defs, safe_to_inject) {
for (var i = 0, len = fn.argnames.length; i < len; i++) {
var arg = fn.argnames[i];
if (arg.__unused) continue;
}
if (in_loop) in_loop.push(arg.definition());
}
- return !in_loop || in_loop.length == 0 || !is_reachable(stat, in_loop);
+ return true;
}
- function flatten_args(fn) {
- var decls = [];
- var expressions = [];
+ function can_inject_vars(catches, safe_to_inject) {
+ var len = fn.body.length;
+ for (var i = 0; i < len; i++) {
+ var stat = fn.body[i];
+ if (!(stat instanceof AST_Definitions)) continue;
+ if (!safe_to_inject) return false;
+ for (var j = stat.definitions.length; --j >= 0;) {
+ var name = stat.definitions[j].name;
+ if (catches[name.name]
+ || identifier_atom(name.name)
+ || scope.var_names()[name.name]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ function can_inject_symbols() {
+ var catches = Object.create(null);
+ do {
+ scope = compressor.parent(++level);
+ if (scope instanceof AST_Catch) {
+ catches[scope.argname.name] = true;
+ } else if (scope instanceof AST_IterationStatement) {
+ in_loop = [];
+ } else if (scope instanceof AST_SymbolRef) {
+ if (scope.fixed_value() instanceof AST_Scope) return false;
+ }
+ } while (!(scope instanceof AST_Scope));
+ var safe_to_inject = !(scope instanceof AST_Toplevel) || compressor.toplevel.vars;
+ var inline = compressor.option("inline");
+ if (!can_inject_vars(catches, !in_loop && inline >= 3 && safe_to_inject)) return false;
+ if (!can_inject_args(catches, in_loop, inline >= 2 && 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();
+ scope.var_names()[name.name] = true;
+ scope.variables.set(name.name, def);
+ scope.enclosed.push(def);
+ decls.push(make_node(AST_VarDef, name, {
+ name: name,
+ value: null
+ }));
+ var sym = make_node(AST_SymbolRef, name, name);
+ def.references.push(sym);
+ if (value) expressions.push(make_node(AST_Assign, self, {
+ operator: "=",
+ left: sym,
+ right: value
+ }));
+ }
+
+ function flatten_args(decls, expressions) {
for (var len = fn.argnames.length, i = len; --i >= 0;) {
var name = fn.argnames[i];
var value = self.args[i];
if (name.__unused || scope.var_names()[name.name]) {
- if (value) {
- expressions.unshift(value);
- }
+ if (value) expressions.push(value);
} else {
- var def = name.definition();
- scope.var_names()[name.name] = true;
- scope.variables.set(name.name, def);
- scope.enclosed.push(def);
var symbol = make_node(AST_SymbolVar, name, name);
- def.orig.push(symbol);
- decls.unshift(make_node(AST_VarDef, name, {
- name: symbol,
- value: null
- }));
- var sym = make_node(AST_SymbolRef, name, name);
- def.references.push(sym);
+ name.definition().orig.push(symbol);
if (!value && in_loop) value = make_node(AST_Undefined, self);
- if (value) expressions.unshift(make_node(AST_Assign, self, {
- operator: "=",
- left: sym,
- right: value
- }));
+ append_var(decls, expressions, symbol, value);
}
}
+ decls.reverse();
+ expressions.reverse();
for (i = len, len = self.args.length; i < len; i++) {
expressions.push(self.args[i]);
}
+ }
+
+ function flatten_body(decls, expressions) {
+ for (i = 0, len = fn.body.length; i < len; i++) {
+ var stat = fn.body[i];
+ if (stat instanceof AST_Definitions) {
+ stat.definitions.forEach(function(var_def) {
+ append_var(decls, expressions, var_def.name, var_def.value);
+ });
+ }
+ }
+ expressions.push(value);
+ }
+
+ function flatten_fn() {
+ var decls = [];
+ var expressions = [];
+ flatten_args(decls, expressions);
+ flatten_body(decls, expressions);
if (decls.length) {
i = scope.body.indexOf(compressor.parent(level - 1)) + 1;
scope.body.splice(i, 0, make_node(AST_Var, fn, {
}
return expressions;
}
-
- function flatten_body(stat) {
- if (stat instanceof AST_Return) {
- return stat.value;
- } else if (stat instanceof AST_SimpleStatement) {
- return make_node(AST_UnaryPrefix, stat, {
- operator: "void",
- expression: stat.body
- });
- }
- }
});
OPT(AST_New, function(self, compressor){