esbuild icon indicating copy to clipboard operation
esbuild copied to clipboard

Comparisons of heterogenous primitive types are not considered for dead code elimination

Open hamish-milne opened this issue 3 years ago • 2 comments

echo "if ('foo'=='bar') foo(); else bar();" | yarn esbuild --minify outputs bar(); as expected.

echo "if ('foo'==null) foo(); else bar();" | yarn esbuild --minify outputs "foo"==null?foo():bar(); when we expect it to output bar();, because 'foo'==null is always false.

This occurs when the LHS and RHS of an equality operator are different types, or arrays, or objects. Additionally, only ==, ===, the logical and ternary operators, and property accessors of new object expressions seem to be considered; arithmetic and comparison operators are excluded. While some of these are probably out of scope, it would be helpful to understand which constructs are eliminated and which are not.

hamish-milne avatar Jul 18 '22 10:07 hamish-milne

The code for this is here:

https://github.com/evanw/esbuild/blob/f730c03dc7be43c3c908fbd6e106120a99ebb4a0/internal/js_ast/js_ast_helpers.go#L840-L878

Code folding for loose comparisons hasn't been implemented yet. Unlike JavaScript minifiers written in JavaScript, doing this in Go is non-trivial because you have to carefully and manually implement JavaScript type coercion rules. Right now constant folding for loose comparisons is only done in cases where both sides are primitives and there is no type coercion.

I'm open to implementing some additional rules. The specific algorithm to use as a guide would be this: https://tc39.es/ecma262/#sec-islooselyequal. Which rules are you looking for? Just equality with null?

evanw avatar Jul 18 '22 14:07 evanw

Loose equality is often discouraged except for null/undefined checks. It seems fine to implement loose equality for these cases. It is pretty straightforward since null/undefined are only loosely equal to null/undefined.

Conaclos avatar Aug 12 '22 11:08 Conaclos