introduce eager evaluation (#3587)
This commit is contained in:
@@ -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
|
- `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
|
- `expression` (default: `false`) -- Pass `true` to preserve completion values
|
||||||
from terminal statements without `return`, e.g. in bookmarklets.
|
from terminal statements without `return`, e.g. in bookmarklets.
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ function Compressor(options, false_by_default) {
|
|||||||
unsafe_undefined: false,
|
unsafe_undefined: false,
|
||||||
unused : !false_by_default,
|
unused : !false_by_default,
|
||||||
}, true);
|
}, true);
|
||||||
|
var evaluate = this.options["evaluate"];
|
||||||
|
this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
|
||||||
var global_defs = this.options["global_defs"];
|
var global_defs = this.options["global_defs"];
|
||||||
if (typeof global_defs == "object") for (var key in global_defs) {
|
if (typeof global_defs == "object") for (var key in global_defs) {
|
||||||
if (/^@/.test(key) && HOP(global_defs, key)) {
|
if (/^@/.test(key) && HOP(global_defs, key)) {
|
||||||
@@ -2676,22 +2678,21 @@ merge(Compressor.prototype, {
|
|||||||
node.DEFMETHOD("_find_defs", func);
|
node.DEFMETHOD("_find_defs", func);
|
||||||
});
|
});
|
||||||
|
|
||||||
function best_of_expression(ast1, ast2) {
|
function best_of_expression(ast1, ast2, threshold) {
|
||||||
return ast1.print_to_string().length >
|
var delta = ast2.print_to_string().length - ast1.print_to_string().length;
|
||||||
ast2.print_to_string().length
|
return delta < (threshold || 0) ? ast2 : ast1;
|
||||||
? ast2 : ast1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function best_of_statement(ast1, ast2) {
|
function best_of_statement(ast1, ast2, threshold) {
|
||||||
return best_of_expression(make_node(AST_SimpleStatement, ast1, {
|
return best_of_expression(make_node(AST_SimpleStatement, ast1, {
|
||||||
body: ast1
|
body: ast1
|
||||||
}), make_node(AST_SimpleStatement, ast2, {
|
}), make_node(AST_SimpleStatement, ast2, {
|
||||||
body: ast2
|
body: ast2
|
||||||
})).body;
|
}), threshold).body;
|
||||||
}
|
}
|
||||||
|
|
||||||
function best_of(compressor, ast1, ast2) {
|
function best_of(compressor, ast1, ast2, threshold) {
|
||||||
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
|
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
function convert_to_predicate(obj) {
|
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 = [
|
var object_fns = [
|
||||||
"constructor",
|
"constructor",
|
||||||
"toString",
|
"toString",
|
||||||
@@ -5366,12 +5374,7 @@ merge(Compressor.prototype, {
|
|||||||
&& is_iife_call(self)) {
|
&& is_iife_call(self)) {
|
||||||
return self.negate(compressor, true);
|
return self.negate(compressor, true);
|
||||||
}
|
}
|
||||||
var ev = self.evaluate(compressor);
|
return try_evaluate(compressor, self);
|
||||||
if (ev !== self) {
|
|
||||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
|
||||||
return best_of(compressor, ev, self);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function return_value(stat) {
|
function return_value(stat) {
|
||||||
if (!stat) return make_node(AST_Undefined, self);
|
if (!stat) return make_node(AST_Undefined, self);
|
||||||
@@ -5702,15 +5705,8 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// avoids infinite recursion of numerals
|
// avoids infinite recursion of numerals
|
||||||
if (self.operator != "-"
|
return self.operator == "-" && (e instanceof AST_Number || e instanceof AST_Infinity)
|
||||||
|| !(e instanceof AST_Number || e instanceof AST_Infinity)) {
|
? self : try_evaluate(compressor, self);
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
|
AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
|
||||||
@@ -6274,12 +6270,7 @@ merge(Compressor.prototype, {
|
|||||||
self.right = self.right.right;
|
self.right = self.right.right;
|
||||||
return self.transform(compressor);
|
return self.transform(compressor);
|
||||||
}
|
}
|
||||||
var ev = self.evaluate(compressor);
|
return try_evaluate(compressor, self);
|
||||||
if (ev !== self) {
|
|
||||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
|
||||||
return best_of(compressor, ev, self);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function align(ref, op) {
|
function align(ref, op) {
|
||||||
switch (ref) {
|
switch (ref) {
|
||||||
@@ -6448,11 +6439,11 @@ merge(Compressor.prototype, {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
var name_length = def.name.length;
|
var name_length = def.name.length;
|
||||||
var overhead = 0;
|
|
||||||
if (compressor.option("unused") && !compressor.exposed(def)) {
|
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 {
|
} else {
|
||||||
def.should_replace = false;
|
def.should_replace = false;
|
||||||
}
|
}
|
||||||
@@ -7046,12 +7037,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ev = self.evaluate(compressor);
|
return try_evaluate(compressor, self);
|
||||||
if (ev !== self) {
|
|
||||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
|
||||||
return best_of(compressor, ev, self);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function find_lambda() {
|
function find_lambda() {
|
||||||
var i = 0, p;
|
var i = 0, p;
|
||||||
@@ -7152,12 +7138,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var sub = self.flatten_object(self.property, compressor);
|
var sub = self.flatten_object(self.property, compressor);
|
||||||
if (sub) return sub.optimize(compressor);
|
if (sub) return sub.optimize(compressor);
|
||||||
var ev = self.evaluate(compressor);
|
return try_evaluate(compressor, self);
|
||||||
if (ev !== self) {
|
|
||||||
ev = make_node_from_constant(ev, self).optimize(compressor);
|
|
||||||
return best_of(compressor, ev, self);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Object, function(self, compressor) {
|
OPT(AST_Object, function(self, compressor) {
|
||||||
|
|||||||
@@ -1921,3 +1921,139 @@ conditional_function: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "42 42"
|
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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
})();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user