}
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) {
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;
}
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;
}
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;
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")
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);
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);
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) {
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();
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
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);
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");
console.log([[1, 2], , [3, 4]][1][1] + 1);
}
expect_stdout: [
+ "1",
+ ",PASS",
"PASS",
"1,2,3,PASS1",
"1,2,3,41",
}
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",
+ ]
+}