fix corner cases related to in (#3964)

This commit is contained in:
Alex Lam S.L
2020-06-07 22:23:23 +01:00
committed by GitHub
parent 28b7b15da1
commit df3bb8028a
7 changed files with 106 additions and 11 deletions

View File

@@ -1464,6 +1464,7 @@ merge(Compressor.prototype, {
} }
function is_last_node(node, parent) { function is_last_node(node, parent) {
if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right.tail_node());
if (node instanceof AST_Call) { if (node instanceof AST_Call) {
var fn = node.expression; var fn = node.expression;
if (fn instanceof AST_SymbolRef) { if (fn instanceof AST_SymbolRef) {
@@ -3824,7 +3825,8 @@ merge(Compressor.prototype, {
def(AST_Assign, return_true); def(AST_Assign, return_true);
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
return this.left.has_side_effects(compressor) return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor); || this.right.has_side_effects(compressor)
|| this.operator == "in" && !is_object(this.right.tail_node());
}); });
def(AST_Block, function(compressor) { def(AST_Block, function(compressor) {
return any(this.body, compressor); return any(this.body, compressor);
@@ -5131,6 +5133,15 @@ merge(Compressor.prototype, {
return this; return this;
}); });
def(AST_Binary, function(compressor, first_in_statement) { def(AST_Binary, function(compressor, first_in_statement) {
if (this.operator == "in" && !is_object(this.right.tail_node())) {
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
if (left === this.left) return this;
var node = this.clone();
node.left = left || make_node(AST_Number, this.left, {
value: 0
});
return node;
}
var right = this.right.drop_side_effect_free(compressor, first_in_statement); var right = this.right.drop_side_effect_free(compressor, first_in_statement);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement); if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
if (lazy_op[this.operator] && !(right instanceof AST_Function)) { if (lazy_op[this.operator] && !(right instanceof AST_Function)) {
@@ -6879,8 +6890,14 @@ merge(Compressor.prototype, {
var indexFns = makePredicate("indexOf lastIndexOf"); var indexFns = makePredicate("indexOf lastIndexOf");
var commutativeOperators = makePredicate("== === != !== * & | ^"); var commutativeOperators = makePredicate("== === != !== * & | ^");
function is_object(node) { function is_object(node) {
while (node instanceof AST_SymbolRef) {
node = node.fixed_value();
if (!node) return false;
node = node.tail_node();
}
return node instanceof AST_Array return node instanceof AST_Array
|| node instanceof AST_Lambda || node instanceof AST_Lambda
|| node instanceof AST_New
|| node instanceof AST_Object; || node instanceof AST_Object;
} }
@@ -6980,7 +6997,7 @@ merge(Compressor.prototype, {
else if (self.left instanceof AST_SymbolRef else if (self.left instanceof AST_SymbolRef
&& self.right instanceof AST_SymbolRef && self.right instanceof AST_SymbolRef
&& self.left.definition() === self.right.definition() && self.left.definition() === self.right.definition()
&& is_object(self.left.fixed_value())) { && is_object(self.left)) {
return make_node(self.operator[0] == "=" ? AST_True : AST_False, self); return make_node(self.operator[0] == "=" ? AST_True : AST_False, self);
} }
break; break;

View File

@@ -305,6 +305,7 @@ function OutputStream(options) {
|| (ch == "/" && ch == prev) || (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last) || ((ch == "+" || ch == "-") && ch == last)
|| str == "--" && last == "!" || str == "--" && last == "!"
|| str == "in" && prev == "/"
|| last == "--" && ch == ">") { || last == "--" && ch == ">") {
OUTPUT += " "; OUTPUT += " ";
current_col++; current_col++;

View File

@@ -8164,3 +8164,34 @@ issue_3927: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
operator_in: {
options = {
collapse_vars: true,
}
input: {
function log(msg) {
console.log(msg);
}
var a = "FAIL";
try {
a = "PASS";
0 in null;
log("FAIL", a);
} catch (e) {}
log(a);
}
expect: {
function log(msg) {
console.log(msg);
}
var a = "FAIL";
try {
a = "PASS";
0 in null;
log("FAIL", a);
} catch (e) {}
log(a);
}
expect_stdout: "PASS"
}

View File

@@ -136,7 +136,7 @@ relational: {
side_effects :true, side_effects :true,
} }
input: { input: {
foo() in foo(); foo() in new foo();
foo() instanceof bar(); foo() instanceof bar();
foo() < "bar"; foo() < "bar";
bar() > foo(); bar() > foo();

View File

@@ -274,3 +274,26 @@ drop_value: {
foo(), bar(); foo(), bar();
} }
} }
operator_in: {
options = {
side_effects: true,
}
input: {
try {
"foo" in true;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect: {
try {
0 in true;
console.log("FAIL");
} catch (e) {
console.log("PASS");
}
}
expect_stdout: "PASS"
}

View File

@@ -486,4 +486,19 @@ describe("operator", function() {
assert.strictEqual(UglifyJS.parse(exp[0]).print_to_string(), exp[1] + ";"); assert.strictEqual(UglifyJS.parse(exp[0]).print_to_string(), exp[1] + ";");
}); });
}); });
it("Should preserve space between /regex/ and `in`", function() {
[
"/regex/ in {}",
"/regex/g in {}",
"0 + /regex/ in {}",
"0 + /regex/g in {}",
].forEach(function(exp) {
var code = UglifyJS.parse(exp).print_to_string();
try {
assert.strictEqual(UglifyJS.parse(code).print_to_string(), code);
} catch (ex) {
assert.fail("Failed to reparse: " + exp + "\n" + ex);
}
});
});
}); });

View File

@@ -168,7 +168,7 @@ var VALUES = [
"this", "this",
]; ];
var BINARY_OPS_NO_COMMA = [ var BINARY_OPS = [
" + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors) " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
" - ", " - ",
"/", "/",
@@ -190,9 +190,14 @@ var BINARY_OPS_NO_COMMA = [
"%", "%",
"&&", "&&",
"||", "||",
"^" ]; "^",
",",
var BINARY_OPS = [","].concat(BINARY_OPS_NO_COMMA); ];
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
BINARY_OPS.push(" in ");
var ASSIGNMENTS = [ var ASSIGNMENTS = [
"=", "=",
@@ -879,7 +884,7 @@ function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
} }
function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow)
+ createBinaryOp(noComma) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; + createBinaryOp(noComma, canThrow) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
} }
function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) { function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
// intentionally generate more hardcore ops // intentionally generate more hardcore ops
@@ -929,9 +934,12 @@ function createValue() {
return VALUES[rng(VALUES.length)]; return VALUES[rng(VALUES.length)];
} }
function createBinaryOp(noComma) { function createBinaryOp(noComma, canThrow) {
if (noComma) return BINARY_OPS_NO_COMMA[rng(BINARY_OPS_NO_COMMA.length)]; var op;
return BINARY_OPS[rng(BINARY_OPS.length)]; do {
op = BINARY_OPS[rng(BINARY_OPS.length)];
} while (noComma && op == "," || !canThrow && op == " in ");
return op;
} }
function createAssignment() { function createAssignment() {