From 88850a6e05b25a4ffa66e729cca56bd15850a756 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 13 Jun 2020 19:50:26 +0100 Subject: enhance `evaluate` (#3995) --- lib/compress.js | 40 ++++++++++++++++++++-------------------- test/compress/evaluate.js | 42 ++++++++++++++++++++++++++++++++++++++++++ test/ufuzz/index.js | 4 ++++ 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 860eae11..cf286187 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -1485,7 +1485,7 @@ merge(Compressor.prototype, { } function is_last_node(node, parent) { - if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right.tail_node()); + if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right); if (node instanceof AST_Call) { var def, fn = node.expression; if (fn instanceof AST_SymbolRef) { @@ -3369,10 +3369,10 @@ merge(Compressor.prototype, { var elements = []; for (var i = 0; i < this.elements.length; i++) { var element = this.elements[i]; - if (element instanceof AST_Hole) continue; + if (element instanceof AST_Hole) return this; var value = element._eval(compressor, ignore_side_effects, cached, depth); if (element === value) return this; - elements[i] = value; + elements.push(value); } return elements; } @@ -3384,16 +3384,11 @@ merge(Compressor.prototype, { for (var i = 0; i < this.properties.length; i++) { var prop = this.properties[i]; var key = prop.key; - if (key instanceof AST_Symbol) { - key = key.name; - } else if (key instanceof AST_Node) { - key = key._eval(compressor, ignore_side_effects, cached, depth); - if (key === prop.key) return this; - } - if (typeof Object.prototype[key] === "function") { - return this; + if (key instanceof AST_Symbol) key = key.name; + if (prop.value instanceof AST_Function) { + if (typeof Object.prototype[key] == "function") return this; + continue; } - if (prop.value instanceof AST_Function) continue; val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth); if (val[key] === prop.value) return this; } @@ -3480,10 +3475,10 @@ merge(Compressor.prototype, { case "&" : result = left & right; break; case "^" : result = left ^ right; break; case "+" : result = left + right; break; + case "-" : result = left - right; break; case "*" : result = left * right; break; case "/" : result = left / right; break; case "%" : result = left % right; break; - case "-" : result = left - right; break; case "<<" : result = left << right; break; case ">>" : result = left >> right; break; case ">>>": result = left >>> right; break; @@ -3495,7 +3490,13 @@ merge(Compressor.prototype, { case "<=" : result = left <= right; break; case ">" : result = left > right; break; case ">=" : result = left >= right; break; - default : return this; + case "in": + if (right && typeof right == "object" && HOP(right, left)) { + result = true; + break; + } + default: + return this; } if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result; if (compressor.option("unsafe_math") @@ -3849,7 +3850,7 @@ merge(Compressor.prototype, { 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.tail_node()); + || this.operator == "in" && !is_object(this.right); }); def(AST_Block, function(compressor) { return any(this.body, compressor); @@ -3962,7 +3963,7 @@ merge(Compressor.prototype, { def(AST_Binary, function(compressor) { return this.left.may_throw(compressor) || this.right.may_throw(compressor) - || this.operator == "in" && !is_object(this.right.tail_node()); + || this.operator == "in" && !is_object(this.right); }); def(AST_Block, function(compressor) { return any(this.body, compressor); @@ -4057,7 +4058,7 @@ merge(Compressor.prototype, { def(AST_Binary, function() { return this.left.is_constant_expression() && this.right.is_constant_expression() - && (this.operator != "in" || is_object(this.right.tail_node())); + && (this.operator != "in" || is_object(this.right)); }); def(AST_Constant, return_true); def(AST_Lambda, function(scope) { @@ -5163,7 +5164,7 @@ merge(Compressor.prototype, { return this; }); def(AST_Binary, function(compressor, first_in_statement) { - if (this.operator == "in" && !is_object(this.right.tail_node())) { + if (this.operator == "in" && !is_object(this.right)) { var left = this.left.drop_side_effect_free(compressor, first_in_statement); if (left === this.left) return this; var node = this.clone(); @@ -6921,10 +6922,9 @@ merge(Compressor.prototype, { var indexFns = makePredicate("indexOf lastIndexOf"); var commutativeOperators = makePredicate("== === != !== * & | ^"); function is_object(node) { - while (node instanceof AST_SymbolRef) { + while ((node = node.tail_node()) instanceof AST_SymbolRef) { node = node.fixed_value(); if (!node) return false; - node = node.tail_node(); } return node instanceof AST_Array || node instanceof AST_Lambda diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index e5ecc6d5..4fecd701 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -560,6 +560,8 @@ unsafe_array: { input: { var a = "PASS"; Array.prototype[1] = a; + console.log([, ].length); + console.log("" + [, , ]); console.log([1, , 3][1]); console.log([1, 2, 3, a] + 1); console.log([1, 2, 3, 4] + 1); @@ -574,6 +576,8 @@ unsafe_array: { expect: { var a = "PASS"; Array.prototype[1] = a; + console.log([, ].length); + console.log("" + [, , ]); console.log([1, , 3][1]); console.log([1, 2, 3, a] + 1); console.log("1,2,3,41"); @@ -586,6 +590,8 @@ unsafe_array: { console.log([[1, 2], , [3, 4]][1][1] + 1); } expect_stdout: [ + "1", + ",PASS", "PASS", "1,2,3,PASS1", "1,2,3,41", @@ -2770,3 +2776,39 @@ issue_3988: { } expect_stdout: "0" } + +operator_in: { + options = { + evaluate: true, + unsafe: true, + } + input: { + Object.prototype.PASS = 0; + console.log(0 in [ 1 ]); + console.log(0 in [ , ]); + console.log(0 / 0 in { NaN: 2 }); + console.log("PASS" in { }); + console.log("FAIL" in { }); + console.log("toString" in { }); + console.log("toString" in { toString: 3 }); + } + expect: { + Object.prototype.PASS = 0; + console.log(true); + console.log(0 in [ , ]); + console.log(true); + console.log("PASS" in { }); + console.log("FAIL" in { }); + console.log("toString" in { }); + console.log(true); + } + expect_stdout: [ + "true", + "false", + "true", + "true", + "false", + "true", + "true", + ] +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index 006b4ca7..1a8eaff5 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -767,6 +767,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); case p++: return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey(); + case p++: + return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow); + case p++: + return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow); case p++: var name = getVarName(); var s = name + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]"; -- cgit v1.2.3