[Pkg-javascript-commits] [uglifyjs] 385/491: inline functions with `AST_Var` (#2688)
Jonas Smedegaard
dr at jones.dk
Wed Feb 14 19:51:55 UTC 2018
This is an automated email from the git hooks/post-receive script.
js pushed a commit to annotated tag debian/3.3.10-1
in repository uglifyjs.
commit 7d3cddf9d624d169c6667a52e8d6f313d1b30159
Author: Alex Lam S.L <alexlamsl at gmail.com>
Date: Wed Jan 3 01:54:44 2018 +0800
inline functions with `AST_Var` (#2688)
---
README.md | 8 +-
lib/compress.js | 174 ++++++++++++++++++++++++----------
test/compress/functions.js | 231 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 360 insertions(+), 53 deletions(-)
diff --git a/README.md b/README.md
index 43793dc..d7c47d7 100644
--- a/README.md
+++ b/README.md
@@ -640,7 +640,13 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `if_return` (default: `true`) -- optimizations for if/return and if/continue
-- `inline` (default: `true`) -- embed simple functions
+- `inline` (default: `true`) -- inline calls to function with simple/`return` statement:
+ - `false` -- same as `0`
+ - `0` -- disabled inlining
+ - `1` -- inline simple functions
+ - `2` -- inline functions with arguments
+ - `3` -- inline functions with arguments and variables
+ - `true` -- same as `3`
- `join_vars` (default: `true`) -- join consecutive `var` statements
diff --git a/lib/compress.js b/lib/compress.js
index 3221268..06873df 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -99,6 +99,7 @@ function Compressor(options, false_by_default) {
});
}
}
+ 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;
@@ -4030,7 +4031,7 @@ merge(Compressor.prototype, {
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
@@ -4038,11 +4039,8 @@ merge(Compressor.prototype, {
&& 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));
@@ -4072,19 +4070,44 @@ merge(Compressor.prototype, {
}
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;
@@ -4096,43 +4119,101 @@ merge(Compressor.prototype, {
}
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, {
@@ -4141,17 +4222,6 @@ merge(Compressor.prototype, {
}
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){
diff --git a/test/compress/functions.js b/test/compress/functions.js
index dd98ba6..f38977b 100644
--- a/test/compress/functions.js
+++ b/test/compress/functions.js
@@ -1694,3 +1694,234 @@ loop_init_arg: {
}
expect_stdout: "PASS"
}
+
+inline_false: {
+ options = {
+ inline: false,
+ side_effects: true,
+ toplevel: true,
+ }
+ input: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ "3",
+ ]
+}
+
+inline_0: {
+ options = {
+ inline: 0,
+ side_effects: true,
+ toplevel: true,
+ }
+ input: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ "3",
+ ]
+}
+
+inline_1: {
+ options = {
+ inline: 1,
+ side_effects: true,
+ toplevel: true,
+ }
+ input: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect: {
+ console.log(1);
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ "3",
+ ]
+}
+
+inline_2: {
+ options = {
+ inline: 2,
+ side_effects: true,
+ toplevel: true,
+ }
+ input: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect: {
+ console.log(1);
+ a = 2, console.log(a);
+ var a;
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ "3",
+ ]
+}
+
+inline_3: {
+ options = {
+ inline: 3,
+ side_effects: true,
+ toplevel: true,
+ }
+ input: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect: {
+ console.log(1);
+ a = 2, console.log(a);
+ var a;
+ b = 3, c = b, console.log(c);
+ var b, c;
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ "3",
+ ]
+}
+
+inline_true: {
+ options = {
+ inline: true,
+ side_effects: true,
+ toplevel: true,
+ }
+ input: {
+ (function() {
+ console.log(1);
+ })();
+ (function(a) {
+ console.log(a);
+ })(2);
+ (function(b) {
+ var c = b;
+ console.log(c);
+ })(3);
+ }
+ expect: {
+ console.log(1);
+ a = 2, console.log(a);
+ var a;
+ b = 3, c = b, console.log(c);
+ var b, c;
+ }
+ expect_stdout: [
+ "1",
+ "2",
+ "3",
+ ]
+}
+
+use_before_init_in_loop: {
+ options = {
+ inline: true,
+ toplevel: true,
+ }
+ input: {
+ var a = "PASS";
+ for (var b = 2; --b >= 0;) (function() {
+ var c = function() {
+ return 1;
+ }(c && (a = "FAIL"));
+ })();
+ console.log(a);
+ }
+ expect: {
+ var a = "PASS";
+ for (var b = 2; --b >= 0;) (function() {
+ var c = (c && (a = "FAIL"), 1);
+ })();
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/uglifyjs.git
More information about the Pkg-javascript-commits
mailing list