introduce spread (#4346)

fixes #4345
This commit is contained in:
Alex Lam S.L
2020-12-07 22:51:20 +00:00
committed by GitHub
parent 336336f53f
commit c587d7917d
4 changed files with 95 additions and 48 deletions

View File

@@ -751,6 +751,8 @@ to be `false` and all symbol names will be omitted.
annotation `/*@__PURE__*/` or `/*#__PURE__*/` immediately precedes the call. For
example: `/*@__PURE__*/foo();`
- `spread` (default: `true`) -- flatten spread expressions.
- `strings` (default: `true`) -- compact string concatenations.
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches

View File

@@ -84,6 +84,7 @@ function Compressor(options, false_by_default) {
reduce_vars : !false_by_default,
sequences : !false_by_default,
side_effects : !false_by_default,
spread : !false_by_default,
strings : !false_by_default,
switches : !false_by_default,
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) {
if (compressor.option("properties")) {
var exp = self.expression;
if (compressor.parent() instanceof AST_Object) {
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) {
var exp = self.expression;
if (compressor.option("spread") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
return List.splice(exp.elements.map(function(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) {
if (!compressor.option("objects") || compressor.has_directive("use strict")) return self;
for (var i = self.properties.length; --i >= 0;) {
var prop = self.properties[i];
var key = prop.key;
if (key instanceof AST_Node) key = key.evaluate(compressor);
if (is_integer(key)) break;
if (key !== prop.key) prop.key = "" + key;
}
if (!compressor.option("objects")) return self;
var changed = false;
var computed_int = false;
var has_computed = false;
var keep_duplicate = compressor.has_directive("use strict");
var keys = new Dictionary();
var values = [];
self.properties.forEach(function(prop) {
if (prop instanceof AST_ObjectKeyVal && typeof prop.key == "string") {
if (prop.value.has_side_effects(compressor)) flush();
keys.add(prop.key, prop.value);
if (!(prop instanceof AST_Spread)) return process(prop);
var exp = prop.expression;
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 {
flush();
values.push(prop);
}
});
flush();
if (self.properties.length != values.length) return make_node(AST_Object, self, {
return changed ? make_node(AST_Object, self, {
properties: values
});
return self;
}) : self;
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, {
key: key,
value: make_sequence(self, expressions)
key: props[0].key,
value: make_sequence(self, props.map(function(prop) {
return prop.value;
}))
}));
if (tail) values.push(tail);
});
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) {

View File

@@ -45,8 +45,8 @@ duplicate_key_strict: {
"use strict";
var o = {
a: 1,
b: 2,
a: 3,
b: 2,
};
for (var k in o)
console.log(k, o[k]);
@@ -323,8 +323,8 @@ issue_4269_3: {
}
expect: {
console.log({
["foo"]: "bar",
get 42() {
foo: "bar",
get [42]() {
return "FAIL";
},
42: "PASS",
@@ -353,8 +353,8 @@ issue_4269_4: {
get 42() {
return "FAIL";
},
["foo"]: "bar",
42: "PASS",
foo: "bar",
[42]: "PASS",
}[42]);
}
expect_stdout: "PASS"

View File

@@ -105,8 +105,8 @@ dont_inline: {
do_inline: {
options = {
properties: true,
inline: true,
spread: true,
}
input: {
console.log(function(a) {
@@ -144,8 +144,8 @@ drop_empty_call_1: {
drop_empty_call_2: {
options = {
properties: true,
side_effects: true,
spread: true,
}
input: {
(function() {})(...[ console.log("PASS") ]);
@@ -159,7 +159,7 @@ drop_empty_call_2: {
convert_hole: {
options = {
properties: true,
spread: true,
}
input: {
console.log(...[ "PASS", , 42 ]);
@@ -295,7 +295,8 @@ keep_getter: {
keep_accessor: {
options = {
properties: true,
objects: true,
spread: true,
}
input: {
var o = {
@@ -372,7 +373,8 @@ unused_var_side_effects: {
issue_4329: {
options = {
properties: true,
objects: true,
spread: true,
}
input: {
console.log({
@@ -423,7 +425,7 @@ issue_4331: {
console.log(b);
}
expect_stdout: "PASS"
node_version: ">=8"
node_version: ">=6"
}
issue_4342: {
@@ -447,3 +449,33 @@ issue_4342: {
expect_stdout: "PASS"
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"
}