Skip to content

Commit

Permalink
lhs_constants option for #1359 (#1361)
Browse files Browse the repository at this point in the history
* First pass at #1359

* Adding test case for lhs_constants
  • Loading branch information
iccir committed Mar 26, 2023
1 parent 5003eb7 commit 36324f1
Show file tree
Hide file tree
Showing 30 changed files with 159 additions and 70 deletions.
6 changes: 5 additions & 1 deletion README.md
Expand Up @@ -708,7 +708,8 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t

- `comparisons` (default: `true`) -- apply certain optimizations to binary nodes,
e.g. `!(a <= b) → a > b` (only when `unsafe_comps`), attempts to negate binary
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc.
nodes, e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. Note: `comparisons`
works best with `lhs_constants` enabled.

- `computed_props` (default: `true`) -- Transforms constant computed properties
into regular ones: `{["computed"]: 1}` is converted to `{computed: 1}`.
Expand Down Expand Up @@ -775,6 +776,9 @@ If you happen to need the source map as a raw object, set `sourceMap.asObject` t
- `keep_infinity` (default: `false`) -- Pass `true` to prevent `Infinity` from
being compressed into `1/0`, which may cause performance issues on Chrome.

- `lhs_constants` (default: `true`) -- Moves constant values to the left-hand side
of binary nodes. `foo == 42 → 42 == foo`

- `loops` (default: `true`) -- optimizations for `do`, `while` and `for` loops
when we can statically determine the condition.

Expand Down
19 changes: 18 additions & 1 deletion lib/compress/index.js
Expand Up @@ -245,6 +245,7 @@ class Compressor extends TreeWalker {
keep_fargs : true,
keep_fnames : false,
keep_infinity : false,
lhs_constants : !false_by_default,
loops : !false_by_default,
module : false,
negate_iife : !false_by_default,
Expand Down Expand Up @@ -2131,7 +2132,7 @@ def_optimize(AST_Binary, function(self, compressor) {
self.right = tmp;
}
}
if (commutativeOperators.has(self.operator)) {
if (compressor.option("lhs_constants") && commutativeOperators.has(self.operator)) {
if (self.right.is_constant()
&& !self.left.is_constant()) {
// if right is a constant, whatever side effects the
Expand Down Expand Up @@ -2161,6 +2162,9 @@ def_optimize(AST_Binary, function(self, compressor) {
// void 0 == x => null == x
if (!is_strict_comparison && is_undefined(self.left, compressor)) {
self.left = make_node(AST_Null, self.left);
// x == void 0 => x == null
} else if (!is_strict_comparison && is_undefined(self.right, compressor)) {
self.right = make_node(AST_Null, self.right);
} else if (compressor.option("typeofs")
// "undefined" == typeof x => undefined === x
&& self.left instanceof AST_String
Expand All @@ -2174,6 +2178,19 @@ def_optimize(AST_Binary, function(self, compressor) {
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
if (self.operator.length == 2) self.operator += "=";
}
} else if (compressor.option("typeofs")
// typeof x === "undefined" => x === undefined
&& self.left instanceof AST_UnaryPrefix
&& self.left.operator == "typeof"
&& self.right instanceof AST_String
&& self.right.value == "undefined") {
var expr = self.left.expression;
if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
self.left = expr;
self.right = make_node(AST_Undefined, self.right).optimize(compressor);
if (self.operator.length == 2) self.operator += "=";
}
} else if (self.left instanceof AST_SymbolRef
// obj !== obj => false
&& self.right instanceof AST_SymbolRef
Expand Down
1 change: 1 addition & 0 deletions test/compress/asm.js
Expand Up @@ -12,6 +12,7 @@ asm_mixed: {
join_vars: true,
keep_fargs: true,
keep_fnames: false,
lhs_constants: true,
loops: true,
negate_iife: true,
properties: true,
Expand Down
4 changes: 2 additions & 2 deletions test/compress/class-properties.js
Expand Up @@ -182,11 +182,11 @@ static_means_execution: {
let x = 0;
// Does not get inlined as it contains an immediate side effect
class WithStaticProps {
static prop = (x = 0 === x ? 1 : "FAIL")
static prop = (x = x === 0 ? 1 : "FAIL")
}
new class {};
new class {
prop = (x = 1 === x ? "PASS" : "FAIL")
prop = (x = x === 1 ? "PASS" : "FAIL")
};
new WithStaticProps();

Expand Down
10 changes: 5 additions & 5 deletions test/compress/collapse_vars.js
Expand Up @@ -473,7 +473,7 @@ collapse_vars_do_while: {
expect: {
function f1(y) {
var c = 9;
do ; while (77 === c);
do ; while (c === 77);
}
function f2(y) {
var c = 5 - y;
Expand Down Expand Up @@ -560,7 +560,7 @@ collapse_vars_do_while_drop_assign: {
expect: {
function f1(y) {
var c = 9;
do ; while (77 === c);
do ; while (c === 77);
}
function f2(y) {
var c = 5 - y;
Expand Down Expand Up @@ -2002,7 +2002,7 @@ var_side_effects_1: {
expect: {
var print = console.log.bind(console);
function foo(x) {
print('Foo:', 2 * x);
print('Foo:', x * 2);
}
foo(10);
}
Expand All @@ -2025,7 +2025,7 @@ var_side_effects_2: {
expect: {
var print = console.log.bind(console);
function foo(x) {
var twice = 2 * x.y;
var twice = x.y * 2;
print('Foo:', twice);
}
foo({ y: 10 });
Expand All @@ -2051,7 +2051,7 @@ var_side_effects_3: {
expect: {
var print = console.log.bind(console);
function foo(x) {
print('Foo:', 2 * x.y);
print('Foo:', x.y * 2);
}
foo({ y: 10 });
}
Expand Down
6 changes: 6 additions & 0 deletions test/compress/comparing.js
Expand Up @@ -116,6 +116,7 @@ self_comparison_2: {
issue_2857_1: {
options = {
comparisons: true,
lhs_constants: true,
}
input: {
function f1(a) {
Expand Down Expand Up @@ -166,6 +167,7 @@ issue_2857_1: {
issue_2857_2: {
options = {
comparisons: true,
lhs_constants: true,
}
input: {
function f(a, p) {
Expand Down Expand Up @@ -196,6 +198,7 @@ issue_2857_2: {
issue_2857_3: {
options = {
comparisons: true,
lhs_constants: true,
}
input: {
function f(a, p) {
Expand Down Expand Up @@ -226,6 +229,7 @@ issue_2857_3: {
issue_2857_4: {
options = {
comparisons: true,
lhs_constants: true,
}
input: {
function f(a, p) {
Expand Down Expand Up @@ -256,6 +260,7 @@ issue_2857_4: {
issue_2857_5: {
options = {
comparisons: true,
lhs_constants: true,
}
input: {
function f(a, p) {
Expand Down Expand Up @@ -286,6 +291,7 @@ issue_2857_5: {
issue_2857_6: {
options = {
comparisons: true,
lhs_constants: true,
pure_getters: "strict",
reduce_vars: true,
}
Expand Down
2 changes: 1 addition & 1 deletion test/compress/conditionals.js
Expand Up @@ -1270,7 +1270,7 @@ issue_2994: {
}
let aValue = 2, anotherValue = 3;
for (let i = 0; i < 8; ++i)
console.log(f(4 & i, 2 & i, 1 & i));
console.log(f(i & 4, i & 2, i & 1));
}
expect_stdout: [
"undefined",
Expand Down
2 changes: 1 addition & 1 deletion test/compress/const.js
Expand Up @@ -98,7 +98,7 @@ issue_1396: {
console.log(3);
console.log(2);
console.log(1);
console.log(1 & a);
console.log(a & 1);
}
function bar() {
const s = "01234567890123456789";
Expand Down
2 changes: 1 addition & 1 deletion test/compress/dead-code.js
Expand Up @@ -1185,7 +1185,7 @@ issue_2860_1: {
}
expect: {
console.log(function(a) {
return 1 ^ a;
return a ^ 1;
}());
}
expect_stdout: "1"
Expand Down
6 changes: 3 additions & 3 deletions test/compress/drop-unused.js
Expand Up @@ -1663,7 +1663,7 @@ issue_2516_1: {
function foo() {
function bar(x) {
var value = (4 - 1) * (x || never_called());
console.log(6 == value ? "PASS" : value);
console.log(value == 6 ? "PASS" : value);
}
Baz = function(x) {
bar.call(null, x);
Expand Down Expand Up @@ -1704,7 +1704,7 @@ issue_2516_2: {
function foo() {
function bar (x) {
var value = (4 - 1) * (x || never_called());
console.log(6 == value ? "PASS" : value);
console.log(value == 6 ? "PASS" : value);
}
Baz = function(x) {
bar.call(null, x);
Expand Down Expand Up @@ -2496,7 +2496,7 @@ issue_t161_top_retain_7: {
}
expect: {
var y = 3;
console.log(2, y, 4, 2 * y, 8, 4 * y);
console.log(2, y, 4, 2 * y, 8, y * 4);
}
expect_stdout: "2 3 4 6 8 12"
}
Expand Down
2 changes: 1 addition & 1 deletion test/compress/functions.js
Expand Up @@ -434,7 +434,7 @@ inner_ref: {
console.log(function (a) {
return a + 1;
}(1), function (a) {
return void 0 === a;
return a === void 0;
}());
}
expect_stdout: "2 true"
Expand Down
2 changes: 1 addition & 1 deletion test/compress/harmony.js
Expand Up @@ -1906,7 +1906,7 @@ issue_t80: {
}
expect: {
function foo(data = []) {
if (1 == arguments.length)
if (arguments.length == 1)
data = [data];
return data;
}
Expand Down
6 changes: 3 additions & 3 deletions test/compress/hoist_props.js
Expand Up @@ -418,7 +418,7 @@ hoist_class: {
}
var o_p = class Foo {
constructor(value) {
this.value = 10 * value;
this.value = value * 10;
}
};
console.log(o_p.name, true, run(o_p, 1), run(o_p, 2));
Expand Down Expand Up @@ -455,7 +455,7 @@ hoist_class_with_new: {
expect: {
var o_p = class Foo {
constructor(value) {
this.value = 10 * value;
this.value = value * 10;
}
};
console.log(o_p.name, true, new o_p(1).value, new o_p(2).value);
Expand Down Expand Up @@ -825,7 +825,7 @@ issue_2519: {
}
expect: {
function testFunc() {
return 1 * ((6 + 5) / 2);
return ((6 + 5) / 2) * 1;
}
console.log(testFunc());
}
Expand Down
4 changes: 2 additions & 2 deletions test/compress/identity.js
Expand Up @@ -206,10 +206,10 @@ inline_identity_lose_this: {
const id = x => x;

const func_bag = {
func: function () { return void 0 === this ? "PASS" : "FAIL"; }
func: function () { return this === void 0 ? "PASS" : "FAIL"; }
};

func_bag.func2 = function () { return void 0 === this ? "PASS" : "FAIL"; };
func_bag.func2 = function () { return this === void 0 ? "PASS" : "FAIL"; };

console.log((0, func_bag.func)());
console.log((0, func_bag.func2)());
Expand Down
2 changes: 1 addition & 1 deletion test/compress/ie8.js
Expand Up @@ -143,7 +143,7 @@ do_screw_try_catch_undefined: {
console.log("caught: "+o)
}
console.log("undefined is " + void 0);
return void 0===o
return o===void 0
}
}
expect_stdout: true
Expand Down
2 changes: 1 addition & 1 deletion test/compress/if_return.js
Expand Up @@ -479,7 +479,7 @@ issue_2747: {
expect: {
"use strict";
function f(baz) {
if (0 === baz) return null;
if (baz === 0) return null;
let r;
return r = baz > 2 ? 4 : 5, r;
}
Expand Down
2 changes: 1 addition & 1 deletion test/compress/issue-1007.js
Expand Up @@ -11,7 +11,7 @@ optional_chaining_boolean_expr: {
}
expect: {
(function(option) {
if ("DIV" !== option.container?.tagName)
if (option.container?.tagName !== "DIV")
throw new Error("Invalid `container` and/or `viewer` option.");
})()
}
Expand Down
2 changes: 1 addition & 1 deletion test/compress/issue-1043.js
Expand Up @@ -18,7 +18,7 @@ issue_1043: {

expect: {
function* range(start = 0, end = null, step = 1) {
if (null == end) {
if (end == null) {
end = start;
start = 0;
}
Expand Down
16 changes: 8 additions & 8 deletions test/compress/issue-1446.js
Expand Up @@ -12,9 +12,9 @@ typeof_eq_undefined: {
var h = "undefined" == typeof i.j;
}
expect: {
var a = "undefined" != typeof b;
b = void 0 !== a;
var c = void 0 !== d.e;
var a = typeof b != "undefined";
b = a !== void 0;
var c = d.e !== void 0;
var f = "undefined" == typeof g;
g = void 0 === f;
var h = void 0 === i.j;
Expand All @@ -36,9 +36,9 @@ typeof_eq_undefined_ie8: {
var h = "undefined" == typeof i.j;
}
expect: {
var a = "undefined" != typeof b;
b = void 0 !== a;
var c = "undefined" != typeof d.e;
var a = typeof b != "undefined";
b = a !== void 0;
var c = typeof d.e != "undefined";
var f = "undefined" == typeof g;
g = void 0 === f;
var h = "undefined" == typeof i.j;
Expand All @@ -56,7 +56,7 @@ undefined_redefined: {
return typeof n == "undefined";
}
}
expect_exact: "function f(undefined){var n=1;return void 0===n}"
expect_exact: "function f(undefined){var n=1;return n===void 0}"
}

undefined_redefined_mangle: {
Expand All @@ -71,5 +71,5 @@ undefined_redefined_mangle: {
return typeof n == "undefined";
}
}
expect_exact: "function f(n){var r=1;return void 0===r}"
expect_exact: "function f(n){var r=1;return r===void 0}"
}
8 changes: 4 additions & 4 deletions test/compress/issue-2871.js
Expand Up @@ -19,10 +19,10 @@ comparison_with_undefined: {
void 0 !== a;
}
expect: {
null == a;
null != a;
void 0 === a;
void 0 !== a;
a == null;
a != null;
a === void 0;
a !== void 0;

null == a;
null != a;
Expand Down
4 changes: 2 additions & 2 deletions test/compress/issue-368.js
Expand Up @@ -38,10 +38,10 @@ collapse: {
}
expect: {
function f1() {
return void 0 !== ('function' === typeof b ? b() : b) && c();
return (typeof b === 'function' ? b() : b) !== void 0 && c();
}
function f2(b) {
return 'stirng' == typeof ('function' === typeof (b = c()) ? b() : b) && d();
return 'stirng' == typeof (typeof (b = c()) === 'function' ? b() : b) && d();
}
function f3(c) {
var a;
Expand Down

0 comments on commit 36324f1

Please sign in to comment.