safer properties transform (#2391)

`{ a: x, b: y }.a` => `[ x, y ][0]`
- `x` cannot be function containing `this`

`[ x, y, z ][1]` => `(x, z, y)`
- only if `z` is side-effect-free
This commit is contained in:
Alex Lam S.L
2017-10-22 20:10:13 +08:00
committed by GitHub
parent 5fd723f143
commit 24aa07855b
6 changed files with 226 additions and 120 deletions

View File

@@ -4452,8 +4452,9 @@ merge(Compressor.prototype, {
});
OPT(AST_Sub, function(self, compressor){
if (compressor.option("properties")) {
var prop = self.property;
if (prop instanceof AST_String && compressor.option("properties")) {
if (prop instanceof AST_String) {
prop = prop.getValue();
if (is_identifier_string(prop)) {
return make_node(AST_Dot, self, {
@@ -4468,6 +4469,41 @@ merge(Compressor.prototype, {
});
}
}
if (prop instanceof AST_Number && self.expression instanceof AST_Array) {
prop = prop.getValue();
var elements = self.expression.elements;
if (prop in elements) {
var flatten = true;
var values = [];
for (var i = elements.length; --i > prop;) {
var value = elements[i].drop_side_effect_free(compressor);
if (value) {
values.unshift(value);
if (flatten && value.has_side_effects(compressor)) flatten = false;
}
}
var retValue = elements[prop];
retValue = retValue instanceof AST_Hole ? make_node(AST_Undefined, retValue) : retValue;
if (!flatten) values.unshift(retValue);
while (--i >= 0) {
var value = elements[i].drop_side_effect_free(compressor);
if (value) values.unshift(value);
else prop--;
}
if (flatten) {
values.push(retValue);
return make_sequence(self, values).optimize(compressor);
} else return make_node(AST_Sub, self, {
expression: make_node(AST_Array, self.expression, {
elements: values
}),
property: make_node(AST_Number, self.property, {
value: prop
})
});
}
}
}
if (is_lhs(self, compressor.parent())) return self;
var ev = self.evaluate(compressor);
if (ev !== self) {
@@ -4493,20 +4529,6 @@ merge(Compressor.prototype, {
if (def) {
return def.optimize(compressor);
}
if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("unsafe") && self.expression instanceof AST_Object) {
var values = self.expression.properties;
for (var i = values.length; --i >= 0;) {
if (values[i].key === self.property) {
var value = values[i].value;
if (value instanceof AST_Function ? value.contains_this() : value.has_side_effects(compressor)) break;
var obj = self.expression.clone();
obj.properties = obj.properties.slice();
obj.properties.splice(i, 1);
return make_sequence(self, [ obj, value ]).optimize(compressor);
}
}
}
if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot
&& self.expression.property == "prototype") {
@@ -4529,6 +4551,30 @@ merge(Compressor.prototype, {
break;
}
}
if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("properties") && self.expression instanceof AST_Object) {
var props = self.expression.properties;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
if (prop.key === self.property) {
if (!all(props, function(prop) {
return prop instanceof AST_ObjectKeyVal;
})) break;
var value = prop.value;
if (value instanceof AST_Function && value.contains_this()) break;
return make_node(AST_Sub, self, {
expression: make_node(AST_Array, self.expression, {
elements: props.map(function(prop) {
return prop.value;
})
}),
property: make_node(AST_Number, self, {
value: i
})
}).optimize(compressor);
}
}
}
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);

View File

@@ -2534,73 +2534,6 @@ issue_2319_3: {
expect_stdout: "true"
}
prop_side_effects_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log({
bar: function() {
return 2;
}
}.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
collapse_vars: true,
evaluate: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
issue_2365: {
options = {
collapse_vars: true,

View File

@@ -386,10 +386,10 @@ unsafe_object_accessor: {
}
}
unsafe_function: {
prop_function: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
properties: true,
}
input: {
console.log(
@@ -402,9 +402,9 @@ unsafe_function: {
expect: {
console.log(
({a:{b:1},b:function(){}}) + 1,
({b:function(){}}, {b:1}) + 1,
({a:{b:1}}, function(){}) + 1,
({b:function(){}}, {b:1}).b + 1
({b:1}) + 1,
function(){} + 1,
2
);
}
expect_stdout: true
@@ -630,10 +630,10 @@ unsafe_string_bad_index: {
expect_stdout: true
}
unsafe_prototype_function: {
prototype_function: {
options = {
evaluate : true,
unsafe : true
evaluate: true,
properties: true,
}
input: {
var a = ({valueOf: 0}) < 1;
@@ -652,8 +652,8 @@ unsafe_prototype_function: {
var d = ({toString: 0}) + "";
var e = (({valueOf: 0}) + "")[2];
var f = (({toString: 0}) + "")[2];
var g = ({}, 0)();
var h = ({}, 0)();
var g = 0();
var h = 0();
}
}

View File

@@ -153,10 +153,10 @@ function_returning_constant_literal: {
options = {
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {

View File

@@ -677,8 +677,8 @@ accessor_this: {
issue_2208_1: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
@@ -696,8 +696,8 @@ issue_2208_1: {
issue_2208_2: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
@@ -721,8 +721,8 @@ issue_2208_2: {
issue_2208_3: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
a = 42;
@@ -746,8 +746,8 @@ issue_2208_3: {
issue_2208_4: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
function foo() {}
@@ -770,8 +770,8 @@ issue_2208_4: {
issue_2208_5: {
options = {
inline: true,
properties: true,
side_effects: true,
unsafe: true,
}
input: {
console.log({
@@ -808,7 +808,7 @@ issue_2256: {
lhs_prop_1: {
options = {
evaluate: true,
unsafe: true,
properties: true,
}
input: {
console.log(++{
@@ -827,9 +827,9 @@ lhs_prop_2: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
side_effects: true,
unsafe: true,
unused: true,
}
input: {
@@ -844,7 +844,7 @@ lhs_prop_2: {
literal_duplicate_key_side_effects: {
options = {
unsafe: true,
properties: true,
}
input: {
console.log({
@@ -853,10 +853,137 @@ literal_duplicate_key_side_effects: {
}.a);
}
expect: {
console.log({
a: "FAIL",
a: console.log ? "PASS" : "FAIL"
}.a);
console.log(console.log ? "PASS" : "FAIL");
}
expect_stdout: "PASS"
}
prop_side_effects_1: {
options = {
evaluate: true,
inline: true,
properties: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
var obj = {
bar: function() {
return 2;
}
};
console.log(obj.bar());
}
expect_stdout: [
"1",
"2",
]
}
prop_side_effects_2: {
options = {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var C = 1;
console.log(C);
var obj = {
bar: function() {
return C + C;
}
};
console.log(obj.bar());
}
expect: {
console.log(1);
console.log(2);
}
expect_stdout: [
"1",
"2",
]
}
accessor_1: {
options = {
properties: true,
}
input: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect: {
console.log({
a: "FAIL",
get a() {
return "PASS";
}
}.a);
}
expect_stdout: "PASS"
node_version: ">=4"
}
accessor_2: {
options = {
properties: true,
}
input: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect: {
console.log({
get a() {
return "PASS";
},
set a(v) {},
a: "FAIL"
}.a);
}
expect_stdout: true
}
array_hole: {
options = {
properties: true,
}
input: {
console.log(
[ 1, 2, , 3][1],
[ 1, 2, , 3][2],
[ 1, 2, , 3][3]
);
}
expect: {
console.log(2, void 0, 3);
}
expect_stdout: "2 undefined 3"
}

View File

@@ -2660,8 +2660,8 @@ obj_var_2: {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
@@ -2716,10 +2716,10 @@ obj_arg_2: {
evaluate: true,
inline: true,
passes: 2,
properties: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {