diff --git a/lib/compress.js b/lib/compress.js index edc7a07f..b19bd893 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 node.operator == "in" && !is_object(node.right); + if (node.TYPE == "Binary") return !can_drop_op(node.operator, node.right); 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) - || this.operator == "in" && !is_object(this.right); + || !can_drop_op(this.operator, this.right); }); 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) - || this.operator == "in" && !is_object(this.right); + || !can_drop_op(this.operator, this.right); }); def(AST_Block, function(compressor) { return any(this.body, compressor); @@ -5822,7 +5822,7 @@ Compressor.prototype.compress = function(node) { def(AST_Binary, function(scope) { return this.left.is_constant_expression(scope) && this.right.is_constant_expression(scope) - && (this.operator != "in" || is_object(this.right)); + && can_drop_op(this.operator, this.right); }); def(AST_Class, function(scope) { var base = this.extends; @@ -8533,7 +8533,7 @@ Compressor.prototype.compress = function(node) { var left = this.left; var right = this.right; var op = this.operator; - if (op == "in" && !is_object(right)) { + if (!can_drop_op(op, right)) { var lhs = left.drop_side_effect_free(compressor, first_in_statement); if (lhs === left) return this; var node = this.clone(); @@ -11202,6 +11202,18 @@ Compressor.prototype.compress = function(node) { || node instanceof AST_Object; } + function can_drop_op(op, rhs) { + switch (op) { + case "in": + return is_object(rhs); + case "instanceof": + if (rhs instanceof AST_SymbolRef) rhs = rhs.fixed_value(); + return is_lambda(rhs); + default: + return true; + } + } + function is_primitive(compressor, node) { if (node.is_constant()) return true; if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right); @@ -11708,6 +11720,12 @@ Compressor.prototype.compress = function(node) { } } break; + case "instanceof": + if (is_lambda(self.right)) return make_sequence(self, [ + self, + make_node(AST_False, self), + ]).optimize(compressor); + break; } if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) { if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) { diff --git a/lib/output.js b/lib/output.js index 55434c1a..02c22e6a 100644 --- a/lib/output.js +++ b/lib/output.js @@ -260,6 +260,15 @@ function OutputStream(options) { var require_semicolon = makePredicate("( [ + * / - , ."); + function require_space(prev, ch, str) { + return is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") + || (ch == "/" && ch == prev) + || ((ch == "+" || ch == "-") && ch == last) + || last == "--" && ch == ">" + || last == "!" && str == "--" + || prev == "/" && (str == "in" || str == "instanceof"); + } + var print = options.beautify || options.comments || options.max_line_len @@ -312,12 +321,7 @@ function OutputStream(options) { } if (might_need_space) { - if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") - || (ch == "/" && ch == prev) - || ((ch == "+" || ch == "-") && ch == last) - || str == "--" && last == "!" - || str == "in" && prev == "/" - || last == "--" && ch == ">") { + if (require_space(prev, ch, str)) { output += " "; current_col++; } @@ -355,14 +359,7 @@ function OutputStream(options) { } } if (might_need_space) { - if (is_identifier_char(prev) && (is_identifier_char(ch) || ch == "\\") - || (ch == "/" && ch == prev) - || ((ch == "+" || ch == "-") && ch == last) - || str == "--" && last == "!" - || str == "in" && prev == "/" - || last == "--" && ch == ">") { - output += " "; - } + if (require_space(prev, ch, str)) output += " "; if (prev != "<" || str != "!") might_need_space = false; } output += str; @@ -1819,9 +1816,6 @@ function OutputStream(options) { case "\u2029": return "\\u2029"; } })); - var p = output.parent(); - if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === this) - output.print(" "); }); function force_statement(stat, output) { diff --git a/test/compress/awaits.js b/test/compress/awaits.js index 9effd679..c05bd5d6 100644 --- a/test/compress/awaits.js +++ b/test/compress/awaits.js @@ -1293,6 +1293,21 @@ functions_inner_var: { node_version: ">=8" } +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof async function() {}); + } + expect: { + console.log(false); + } + expect_stdout: "false" + node_version: ">=8" +} + issue_4335_1: { options = { inline: true, diff --git a/test/compress/classes.js b/test/compress/classes.js index a1af2560..6695ccde 100644 --- a/test/compress/classes.js +++ b/test/compress/classes.js @@ -1266,6 +1266,23 @@ keep_fnames: { node_version: ">=4" } +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + "use strict"; + console.log(42 instanceof class {}); + } + expect: { + "use strict"; + console.log(false); + } + expect_stdout: "false" + node_version: ">=4" +} + issue_805_1: { options = { inline: true, diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index 0f8cfaf6..9efbcf36 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -907,6 +907,20 @@ chained_side_effects: { ] } +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof function() {}); + } + expect: { + console.log(false); + } + expect_stdout: "false" +} + issue_1649: { options = { evaluate: true, diff --git a/test/compress/pure_funcs.js b/test/compress/pure_funcs.js index bf38003c..688a1057 100644 --- a/test/compress/pure_funcs.js +++ b/test/compress/pure_funcs.js @@ -147,7 +147,7 @@ relational: { "bar" >= "bar"; } expect: { - bar(); + 0 instanceof bar(); bar(); bar(), bar(); bar(); diff --git a/test/compress/regexp.js b/test/compress/regexp.js index 3194d38e..3324a0c8 100644 --- a/test/compress/regexp.js +++ b/test/compress/regexp.js @@ -50,6 +50,22 @@ regexp_properties: { expect_stdout: "abc true false 0 false" } +instanceof_1: { + input: { + console.log(/foo/ instanceof RegExp); + } + expect_exact: "console.log(/foo/ instanceof RegExp);" + expect_stdout: "true" +} + +instanceof_2: { + input: { + console.log(42 + /foo/ instanceof Object); + } + expect_exact: "console.log(42+/foo/ instanceof Object);" + expect_stdout: "false" +} + issue_3434_1: { options = { evaluate: true, diff --git a/test/compress/side_effects.js b/test/compress/side_effects.js index ca929153..cc927b50 100644 --- a/test/compress/side_effects.js +++ b/test/compress/side_effects.js @@ -645,3 +645,56 @@ issue_4751: { } expect_stdout: "PASS" } + +drop_instanceof: { + options = { + side_effects: true, + } + input: { + 42 instanceof function() {}; + console.log("PASS"); + } + expect: { + console.log("PASS"); + } + expect_stdout: "PASS" +} + +drop_instanceof_reference: { + options = { + reduce_vars: true, + side_effects: true, + toplevel: true, + } + input: { + function f() {} + 42 instanceof f; + console.log("PASS"); + } + expect: { + function f() {} + console.log("PASS"); + } + expect_stdout: "PASS" +} + +retain_instanceof: { + options = { + side_effects: true, + } + input: { + try { + 42 instanceof "foo"; + } catch (e) { + console.log("PASS"); + } + } + expect: { + try { + 0 instanceof "foo"; + } catch (e) { + console.log("PASS"); + } + } + expect_stdout: "PASS" +} diff --git a/test/compress/yields.js b/test/compress/yields.js index 6021ad07..fbbe51a5 100644 --- a/test/compress/yields.js +++ b/test/compress/yields.js @@ -934,6 +934,21 @@ drop_unused_call: { node_version: ">=4" } +instanceof_lambda: { + options = { + evaluate: true, + side_effects: true, + } + input: { + console.log(42 instanceof function*() {}); + } + expect: { + console.log(false); + } + expect_stdout: "false" + node_version: ">=4" +} + issue_4454_1: { rename = false options = { diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index 4c981fa3..c8ee64dc 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -253,7 +253,7 @@ BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); if (SUPPORT.exponentiation) BINARY_OPS.push("**"); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); BINARY_OPS = BINARY_OPS.concat(BINARY_OPS); -BINARY_OPS.push(" in "); +BINARY_OPS.push(" in ", " instanceof "); var ASSIGNMENTS = [ "=" ]; ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS); @@ -2009,7 +2009,7 @@ function createBinaryOp(noComma, canThrow) { var op; do { op = BINARY_OPS[rng(BINARY_OPS.length)]; - } while (noComma && op == "," || !canThrow && op == " in "); + } while (noComma && op == "," || !canThrow && /^ in/.test(op)); return op; }