diff --git a/README.md b/README.md index 2a3c4de6c80..60ee8145ca0 100644 --- a/README.md +++ b/README.md @@ -818,8 +818,9 @@ to be `false` and all symbol names will be omitted. - `unsafe` (default: `false`) — apply "unsafe" transformations (discussion below) -- `unsafe_comps` (default: `false`) — compress expressions like `a <= b` assuming - none of the operands can be (coerced to) `NaN`. +- `unsafe_comps` (default: `false`) — assume operands cannot be (coerced to) `NaN` + in numeric comparisons, e.g. `a <= b`. In addition, expressions involving `in` + or `instanceof` would never throw. - `unsafe_Function` (default: `false`) — compress and mangle `Function(args, code)` when both `args` and `code` are string literals. diff --git a/lib/compress.js b/lib/compress.js index b19bd893b68..fbb6aa2a7dd 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2452,7 +2452,7 @@ Compressor.prototype.compress = function(node) { function is_last_node(node, parent) { if (node instanceof AST_Await) return true; - if (node.TYPE == "Binary") return !can_drop_op(node.operator, node.right); + if (node.TYPE == "Binary") return !can_drop_op(node.operator, node.right, compressor); if (node instanceof AST_Call) { var def, fn = node.expression; if (fn instanceof AST_SymbolRef) { @@ -5551,7 +5551,7 @@ Compressor.prototype.compress = function(node) { def(AST_Binary, function(compressor) { return this.left.has_side_effects(compressor) || this.right.has_side_effects(compressor) - || !can_drop_op(this.operator, this.right); + || !can_drop_op(this.operator, this.right, compressor); }); def(AST_Block, function(compressor) { return any(this.body, compressor); @@ -5705,7 +5705,7 @@ Compressor.prototype.compress = function(node) { def(AST_Binary, function(compressor) { return this.left.may_throw(compressor) || this.right.may_throw(compressor) - || !can_drop_op(this.operator, this.right); + || !can_drop_op(this.operator, this.right, compressor); }); def(AST_Block, function(compressor) { return any(this.body, compressor); @@ -8533,7 +8533,7 @@ Compressor.prototype.compress = function(node) { var left = this.left; var right = this.right; var op = this.operator; - if (!can_drop_op(op, right)) { + if (!can_drop_op(op, right, compressor)) { var lhs = left.drop_side_effect_free(compressor, first_in_statement); if (lhs === left) return this; var node = this.clone(); @@ -11202,13 +11202,13 @@ Compressor.prototype.compress = function(node) { || node instanceof AST_Object; } - function can_drop_op(op, rhs) { + function can_drop_op(op, rhs, compressor) { switch (op) { case "in": - return is_object(rhs); + return is_object(rhs) || compressor && compressor.option("unsafe_comps"); case "instanceof": if (rhs instanceof AST_SymbolRef) rhs = rhs.fixed_value(); - return is_lambda(rhs); + return is_lambda(rhs) || compressor && compressor.option("unsafe_comps"); default: return true; } diff --git a/test/compress/comparisons.js b/test/compress/comparisons.js index 64c75837e57..66602efc81b 100644 --- a/test/compress/comparisons.js +++ b/test/compress/comparisons.js @@ -40,6 +40,22 @@ unsafe_comps: { } } +unsafe_in_instanceof: { + options = { + side_effects: true, + unsafe_comps: true, + } + input: { + var a; + 42 in a; + f() instanceof "foo"; + } + expect: { + var a; + f(); + } +} + dont_change_in_or_instanceof_expressions: { input: { 1 in 1;