@@ -751,6 +751,8 @@ to be `false` and all symbol names will be omitted.
|
|||||||
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
|
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
|
||||||
example: `/*@__PURE__*/foo();`
|
example: `/*@__PURE__*/foo();`
|
||||||
|
|
||||||
|
- `spread` (default: `true`) -- flatten spread expressions.
|
||||||
|
|
||||||
- `strings` (default: `true`) -- compact string concatenations.
|
- `strings` (default: `true`) -- compact string concatenations.
|
||||||
|
|
||||||
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ function Compressor(options, false_by_default) {
|
|||||||
reduce_vars : !false_by_default,
|
reduce_vars : !false_by_default,
|
||||||
sequences : !false_by_default,
|
sequences : !false_by_default,
|
||||||
side_effects : !false_by_default,
|
side_effects : !false_by_default,
|
||||||
|
spread : !false_by_default,
|
||||||
strings : !false_by_default,
|
strings : !false_by_default,
|
||||||
switches : !false_by_default,
|
switches : !false_by_default,
|
||||||
top_retain : null,
|
top_retain : null,
|
||||||
@@ -9724,27 +9725,10 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function is_integer(key) {
|
|
||||||
return /^[0-9]+$/.test(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
OPT(AST_Spread, function(self, compressor) {
|
OPT(AST_Spread, function(self, compressor) {
|
||||||
if (compressor.option("properties")) {
|
var exp = self.expression;
|
||||||
var exp = self.expression;
|
if (compressor.option("spread") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
|
||||||
if (compressor.parent() instanceof AST_Object) {
|
return List.splice(exp.elements.map(function(node) {
|
||||||
if (exp instanceof AST_Object && all(exp.properties, function(node) {
|
|
||||||
return node instanceof AST_ObjectKeyVal;
|
|
||||||
})) return List.splice(exp.properties.map(function(node) {
|
|
||||||
var key = node.key;
|
|
||||||
if (!(key instanceof AST_Node) && is_integer(key)) {
|
|
||||||
node = node.clone();
|
|
||||||
node.key = make_node(AST_Number, node, {
|
|
||||||
value: +key
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}));
|
|
||||||
} else if (exp instanceof AST_Array) return List.splice(exp.elements.map(function(node) {
|
|
||||||
return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
|
return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -10014,40 +9998,69 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Object, function(self, compressor) {
|
OPT(AST_Object, function(self, compressor) {
|
||||||
if (!compressor.option("objects") || compressor.has_directive("use strict")) return self;
|
if (!compressor.option("objects")) return self;
|
||||||
for (var i = self.properties.length; --i >= 0;) {
|
var changed = false;
|
||||||
var prop = self.properties[i];
|
var computed_int = false;
|
||||||
var key = prop.key;
|
var has_computed = false;
|
||||||
if (key instanceof AST_Node) key = key.evaluate(compressor);
|
var keep_duplicate = compressor.has_directive("use strict");
|
||||||
if (is_integer(key)) break;
|
|
||||||
if (key !== prop.key) prop.key = "" + key;
|
|
||||||
}
|
|
||||||
var keys = new Dictionary();
|
var keys = new Dictionary();
|
||||||
var values = [];
|
var values = [];
|
||||||
self.properties.forEach(function(prop) {
|
self.properties.forEach(function(prop) {
|
||||||
if (prop instanceof AST_ObjectKeyVal && typeof prop.key == "string") {
|
if (!(prop instanceof AST_Spread)) return process(prop);
|
||||||
if (prop.value.has_side_effects(compressor)) flush();
|
var exp = prop.expression;
|
||||||
keys.add(prop.key, prop.value);
|
if (exp instanceof AST_Object && all(exp.properties, function(node) {
|
||||||
|
return node instanceof AST_ObjectKeyVal;
|
||||||
|
})) {
|
||||||
|
changed = true;
|
||||||
|
has_computed = true;
|
||||||
|
exp.properties.forEach(process);
|
||||||
} else {
|
} else {
|
||||||
flush();
|
flush();
|
||||||
values.push(prop);
|
values.push(prop);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
flush();
|
flush();
|
||||||
if (self.properties.length != values.length) return make_node(AST_Object, self, {
|
return changed ? make_node(AST_Object, self, {
|
||||||
properties: values
|
properties: values
|
||||||
});
|
}) : self;
|
||||||
return self;
|
|
||||||
|
|
||||||
function flush() {
|
function flush() {
|
||||||
keys.each(function(expressions, key) {
|
keys.each(function(props) {
|
||||||
|
if (props.length == 1) return values.push(props[0]);
|
||||||
|
changed = true;
|
||||||
|
var tail = keep_duplicate && props.pop();
|
||||||
values.push(make_node(AST_ObjectKeyVal, self, {
|
values.push(make_node(AST_ObjectKeyVal, self, {
|
||||||
key: key,
|
key: props[0].key,
|
||||||
value: make_sequence(self, expressions)
|
value: make_sequence(self, props.map(function(prop) {
|
||||||
|
return prop.value;
|
||||||
|
}))
|
||||||
}));
|
}));
|
||||||
|
if (tail) values.push(tail);
|
||||||
});
|
});
|
||||||
keys = new Dictionary();
|
keys = new Dictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function process(prop) {
|
||||||
|
var key = prop.key;
|
||||||
|
if (key instanceof AST_Node) {
|
||||||
|
has_computed = true;
|
||||||
|
key = key.evaluate(compressor);
|
||||||
|
if (key !== prop.key) key = prop.key = "" + key;
|
||||||
|
}
|
||||||
|
if (prop instanceof AST_ObjectKeyVal && typeof key == "string") {
|
||||||
|
if (prop.value.has_side_effects(compressor)) flush();
|
||||||
|
keys.add(key, prop);
|
||||||
|
} else {
|
||||||
|
flush();
|
||||||
|
values.push(prop);
|
||||||
|
}
|
||||||
|
if (has_computed && !computed_int && typeof key == "string" && /^[0-9]+$/.test(key)) {
|
||||||
|
computed_int = true;
|
||||||
|
prop.key = make_node(AST_Number, prop, {
|
||||||
|
value: +key
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Return, function(self, compressor) {
|
OPT(AST_Return, function(self, compressor) {
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ duplicate_key_strict: {
|
|||||||
"use strict";
|
"use strict";
|
||||||
var o = {
|
var o = {
|
||||||
a: 1,
|
a: 1,
|
||||||
b: 2,
|
|
||||||
a: 3,
|
a: 3,
|
||||||
|
b: 2,
|
||||||
};
|
};
|
||||||
for (var k in o)
|
for (var k in o)
|
||||||
console.log(k, o[k]);
|
console.log(k, o[k]);
|
||||||
@@ -323,8 +323,8 @@ issue_4269_3: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log({
|
console.log({
|
||||||
["foo"]: "bar",
|
foo: "bar",
|
||||||
get 42() {
|
get [42]() {
|
||||||
return "FAIL";
|
return "FAIL";
|
||||||
},
|
},
|
||||||
42: "PASS",
|
42: "PASS",
|
||||||
@@ -353,8 +353,8 @@ issue_4269_4: {
|
|||||||
get 42() {
|
get 42() {
|
||||||
return "FAIL";
|
return "FAIL";
|
||||||
},
|
},
|
||||||
["foo"]: "bar",
|
foo: "bar",
|
||||||
42: "PASS",
|
[42]: "PASS",
|
||||||
}[42]);
|
}[42]);
|
||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ dont_inline: {
|
|||||||
|
|
||||||
do_inline: {
|
do_inline: {
|
||||||
options = {
|
options = {
|
||||||
properties: true,
|
|
||||||
inline: true,
|
inline: true,
|
||||||
|
spread: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log(function(a) {
|
console.log(function(a) {
|
||||||
@@ -144,8 +144,8 @@ drop_empty_call_1: {
|
|||||||
|
|
||||||
drop_empty_call_2: {
|
drop_empty_call_2: {
|
||||||
options = {
|
options = {
|
||||||
properties: true,
|
|
||||||
side_effects: true,
|
side_effects: true,
|
||||||
|
spread: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
(function() {})(...[ console.log("PASS") ]);
|
(function() {})(...[ console.log("PASS") ]);
|
||||||
@@ -159,7 +159,7 @@ drop_empty_call_2: {
|
|||||||
|
|
||||||
convert_hole: {
|
convert_hole: {
|
||||||
options = {
|
options = {
|
||||||
properties: true,
|
spread: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log(...[ "PASS", , 42 ]);
|
console.log(...[ "PASS", , 42 ]);
|
||||||
@@ -295,7 +295,8 @@ keep_getter: {
|
|||||||
|
|
||||||
keep_accessor: {
|
keep_accessor: {
|
||||||
options = {
|
options = {
|
||||||
properties: true,
|
objects: true,
|
||||||
|
spread: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = {
|
var o = {
|
||||||
@@ -372,7 +373,8 @@ unused_var_side_effects: {
|
|||||||
|
|
||||||
issue_4329: {
|
issue_4329: {
|
||||||
options = {
|
options = {
|
||||||
properties: true,
|
objects: true,
|
||||||
|
spread: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
console.log({
|
console.log({
|
||||||
@@ -423,7 +425,7 @@ issue_4331: {
|
|||||||
console.log(b);
|
console.log(b);
|
||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
node_version: ">=8"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
issue_4342: {
|
issue_4342: {
|
||||||
@@ -447,3 +449,33 @@ issue_4342: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4345: {
|
||||||
|
options = {
|
||||||
|
objects: true,
|
||||||
|
spread: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log({
|
||||||
|
...{
|
||||||
|
get 42() {
|
||||||
|
return "FAIL";
|
||||||
|
},
|
||||||
|
...{},
|
||||||
|
42: "PASS",
|
||||||
|
},
|
||||||
|
}[42]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log({
|
||||||
|
...{
|
||||||
|
get 42() {
|
||||||
|
return "FAIL";
|
||||||
|
},
|
||||||
|
[42]: "PASS",
|
||||||
|
},
|
||||||
|
}[42]);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user