aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2019-11-16 06:10:47 +0800
committerGitHub <noreply@github.com>2019-11-16 06:10:47 +0800
commit552be61c4d6756fb31675fee0b1284e23d5d8721 (patch)
tree4e7cf68f49301fe0e4cf7707c7c78ef68bb553cf
parentdcfc4aca5b97419150e742f2f94c40cdd8497189 (diff)
downloadtracifyjs-552be61c4d6756fb31675fee0b1284e23d5d8721.tar.gz
tracifyjs-552be61c4d6756fb31675fee0b1284e23d5d8721.zip
introduce eager evaluation (#3587)
-rw-r--r--README.md5
-rw-r--r--lib/compress.js69
-rw-r--r--test/compress/evaluate.js136
-rw-r--r--test/compress/issue-1609.js27
4 files changed, 192 insertions, 45 deletions
diff --git a/README.md b/README.md
index f686889d..f86bf7ad 100644
--- a/README.md
+++ b/README.md
@@ -631,7 +631,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `drop_debugger` (default: `true`) -- remove `debugger;` statements
-- `evaluate` (default: `true`) -- attempt to evaluate constant expressions
+- `evaluate` (default: `true`) -- Evaluate expression for shorter constant
+ representation. Pass `"eager"` to always replace function calls whenever
+ possible, or a positive integer to specify an upper bound for each individual
+ evaluation in number of characters.
- `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
diff --git a/lib/compress.js b/lib/compress.js
index 9d844b81..f82a165e 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -96,6 +96,8 @@ function Compressor(options, false_by_default) {
unsafe_undefined: false,
unused : !false_by_default,
}, true);
+ var evaluate = this.options["evaluate"];
+ this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) {
if (/^@/.test(key) && HOP(global_defs, key)) {
@@ -2676,22 +2678,21 @@ merge(Compressor.prototype, {
node.DEFMETHOD("_find_defs", func);
});
- function best_of_expression(ast1, ast2) {
- return ast1.print_to_string().length >
- ast2.print_to_string().length
- ? ast2 : ast1;
+ function best_of_expression(ast1, ast2, threshold) {
+ var delta = ast2.print_to_string().length - ast1.print_to_string().length;
+ return delta < (threshold || 0) ? ast2 : ast1;
}
- function best_of_statement(ast1, ast2) {
+ function best_of_statement(ast1, ast2, threshold) {
return best_of_expression(make_node(AST_SimpleStatement, ast1, {
body: ast1
}), make_node(AST_SimpleStatement, ast2, {
body: ast2
- })).body;
+ }), threshold).body;
}
- function best_of(compressor, ast1, ast2) {
- return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
+ function best_of(compressor, ast1, ast2, threshold) {
+ return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
}
function convert_to_predicate(obj) {
@@ -2700,6 +2701,13 @@ merge(Compressor.prototype, {
});
}
+ function try_evaluate(compressor, node) {
+ var ev = node.evaluate(compressor);
+ if (ev === node) return node;
+ ev = make_node_from_constant(ev, node).optimize(compressor);
+ return best_of(compressor, node, ev, compressor.eval_threshold);
+ }
+
var object_fns = [
"constructor",
"toString",
@@ -5366,12 +5374,7 @@ merge(Compressor.prototype, {
&& is_iife_call(self)) {
return self.negate(compressor, true);
}
- var ev = self.evaluate(compressor);
- if (ev !== self) {
- ev = make_node_from_constant(ev, self).optimize(compressor);
- return best_of(compressor, ev, self);
- }
- return self;
+ return try_evaluate(compressor, self);
function return_value(stat) {
if (!stat) return make_node(AST_Undefined, self);
@@ -5702,15 +5705,8 @@ merge(Compressor.prototype, {
});
}
// avoids infinite recursion of numerals
- if (self.operator != "-"
- || !(e instanceof AST_Number || e instanceof AST_Infinity)) {
- var ev = self.evaluate(compressor);
- if (ev !== self) {
- ev = make_node_from_constant(ev, self).optimize(compressor);
- return best_of(compressor, ev, self);
- }
- }
- return self;
+ return self.operator == "-" && (e instanceof AST_Number || e instanceof AST_Infinity)
+ ? self : try_evaluate(compressor, self);
});
AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
@@ -6274,12 +6270,7 @@ merge(Compressor.prototype, {
self.right = self.right.right;
return self.transform(compressor);
}
- var ev = self.evaluate(compressor);
- if (ev !== self) {
- ev = make_node_from_constant(ev, self).optimize(compressor);
- return best_of(compressor, ev, self);
- }
- return self;
+ return try_evaluate(compressor, self);
function align(ref, op) {
switch (ref) {
@@ -6448,11 +6439,11 @@ merge(Compressor.prototype, {
};
}
var name_length = def.name.length;
- var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(def)) {
- overhead = (name_length + 2 + value_length) / (def.references.length - def.assignments);
+ name_length += (name_length + 2 + value_length) / (def.references.length - def.assignments);
}
- def.should_replace = value_length <= name_length + overhead ? fn : false;
+ var delta = value_length - Math.floor(name_length);
+ def.should_replace = delta < compressor.eval_threshold ? fn : false;
} else {
def.should_replace = false;
}
@@ -7046,12 +7037,7 @@ merge(Compressor.prototype, {
});
}
}
- var ev = self.evaluate(compressor);
- if (ev !== self) {
- ev = make_node_from_constant(ev, self).optimize(compressor);
- return best_of(compressor, ev, self);
- }
- return self;
+ return try_evaluate(compressor, self);
function find_lambda() {
var i = 0, p;
@@ -7152,12 +7138,7 @@ merge(Compressor.prototype, {
}
var sub = self.flatten_object(self.property, compressor);
if (sub) return sub.optimize(compressor);
- var ev = self.evaluate(compressor);
- if (ev !== self) {
- ev = make_node_from_constant(ev, self).optimize(compressor);
- return best_of(compressor, ev, self);
- }
- return self;
+ return try_evaluate(compressor, self);
});
OPT(AST_Object, function(self, compressor) {
diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js
index ea707e63..94258049 100644
--- a/test/compress/evaluate.js
+++ b/test/compress/evaluate.js
@@ -1921,3 +1921,139 @@ conditional_function: {
}
expect_stdout: "42 42"
}
+
+best_of_evaluate: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function d(x, y) {
+ return x / y;
+ }
+ console.log(0 / 3, 1 / 64, 4 / 7, 7 / 7);
+ console.log(d(0, 3), d(1, 64), d(4, 7), d(7, 7));
+ }
+ expect: {
+ function d(x, y) {
+ return x / y;
+ }
+ console.log(0, 1 / 64, 4 / 7, 1);
+ console.log(0, .015625, d(4, 7), 1);
+ }
+ expect_stdout: [
+ "0 0.015625 0.5714285714285714 1",
+ "0 0.015625 0.5714285714285714 1",
+ ]
+}
+
+eager_evaluate: {
+ options = {
+ evaluate: "eager",
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function d(x, y) {
+ return x / y;
+ }
+ console.log(0 / 3, 1 / 64, 4 / 7, 7 / 7);
+ console.log(d(0, 3), d(1, 64), d(4, 7), d(7, 7));
+ }
+ expect: {
+ console.log(0, .015625, .5714285714285714, 1);
+ console.log(0, .015625, .5714285714285714, 1);
+ }
+ expect_stdout: [
+ "0 0.015625 0.5714285714285714 1",
+ "0 0.015625 0.5714285714285714 1",
+ ]
+}
+
+threshold_evaluate_default: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
+ }
+ expect: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log("111", 6, b(b(b("ABCDEFGHIJK"))));
+ }
+ expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
+}
+
+threshold_evaluate_30: {
+ options = {
+ evaluate: 30,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
+ }
+ expect: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log("111", 6, b(b("ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK")));
+ }
+ expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
+}
+
+threshold_evaluate_100: {
+ options = {
+ evaluate: 100,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
+ }
+ expect: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log("111", 6, b("ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"));
+ }
+ expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
+}
+
+threshold_evaluate_999: {
+ options = {
+ evaluate: 999,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ function b(x) {
+ return x + x + x;
+ }
+ console.log(b("1"), b(2), b(b(b("ABCDEFGHIJK"))));
+ }
+ expect: {
+ console.log("111", 6, "ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK");
+ }
+ expect_stdout: "111 6 ABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJKABCDEFGHIJK"
+}
diff --git a/test/compress/issue-1609.js b/test/compress/issue-1609.js
index d8e82263..17fbb0e7 100644
--- a/test/compress/issue-1609.js
+++ b/test/compress/issue-1609.js
@@ -52,3 +52,30 @@ chained_evaluation_2: {
})();
}
}
+
+chained_evaluation_3: {
+ options = {
+ collapse_vars: true,
+ evaluate: 10,
+ reduce_funcs: true,
+ reduce_vars: true,
+ unused: true,
+ }
+ input: {
+ (function() {
+ var a = "long piece of string";
+ (function() {
+ var b = a, c;
+ c = f(b);
+ c.bar = b;
+ })();
+ })();
+ }
+ expect: {
+ (function() {
+ (function() {
+ f("long piece of string").bar = "long piece of string";
+ })();
+ })();
+ }
+}