improve AST_ConciseMethod compression (#2202)
p(){return x;} ---> p:()=>x
Optimization subject to the `compress` option `arrows`.
This commit is contained in:
@@ -4596,7 +4596,7 @@ merge(Compressor.prototype, {
|
|||||||
var has_special_symbol = false;
|
var has_special_symbol = false;
|
||||||
self.walk(new TreeWalker(function(node) {
|
self.walk(new TreeWalker(function(node) {
|
||||||
if (has_special_symbol) return true;
|
if (has_special_symbol) return true;
|
||||||
if (node instanceof AST_Symbol && !node.definition()) {
|
if (node instanceof AST_Super || node instanceof AST_This) {
|
||||||
has_special_symbol = true;
|
has_special_symbol = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -4656,6 +4656,36 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Lambda.DEFMETHOD("contains_this", function() {
|
||||||
|
var result;
|
||||||
|
var self = this;
|
||||||
|
self.walk(new TreeWalker(function(node) {
|
||||||
|
if (result) return true;
|
||||||
|
if (node instanceof AST_This) return result = true;
|
||||||
|
if (node !== self && node instanceof AST_Scope && !(node instanceof AST_Arrow)) return true;
|
||||||
|
}));
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
OPT(AST_ConciseMethod, function(self, compressor){
|
||||||
|
// p(){return x;} ---> p:()=>x
|
||||||
|
if (compressor.option("arrows")
|
||||||
|
&& compressor.parent() instanceof AST_Object
|
||||||
|
&& self.value.body.length == 1
|
||||||
|
&& self.value.body[0] instanceof AST_Return
|
||||||
|
&& self.value.body[0].value
|
||||||
|
&& !self.value.contains_this()) {
|
||||||
|
var arrow = make_node(AST_Arrow, self.value, self.value);
|
||||||
|
arrow.async = self.async;
|
||||||
|
arrow.is_generator = self.is_generator;
|
||||||
|
return make_node(AST_ObjectKeyVal, self, {
|
||||||
|
key: self.key instanceof AST_SymbolMethod ? self.key.name : self.key,
|
||||||
|
value: arrow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_ObjectKeyVal, function(self, compressor){
|
OPT(AST_ObjectKeyVal, function(self, compressor){
|
||||||
// p:function(){} ---> p(){}
|
// p:function(){} ---> p(){}
|
||||||
// p:function*(){} ---> *p(){}
|
// p:function*(){} ---> *p(){}
|
||||||
@@ -4667,7 +4697,7 @@ merge(Compressor.prototype, {
|
|||||||
var value = self.value;
|
var value = self.value;
|
||||||
var is_arrow_with_block = value instanceof AST_Arrow
|
var is_arrow_with_block = value instanceof AST_Arrow
|
||||||
&& Array.isArray(value.body)
|
&& Array.isArray(value.body)
|
||||||
&& !contains_this(value);
|
&& !value.contains_this();
|
||||||
if ((is_arrow_with_block || value instanceof AST_Function) && !value.name) {
|
if ((is_arrow_with_block || value instanceof AST_Function) && !value.name) {
|
||||||
return make_node(AST_ConciseMethod, self, {
|
return make_node(AST_ConciseMethod, self, {
|
||||||
async: value.async,
|
async: value.async,
|
||||||
@@ -4680,16 +4710,5 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
|
||||||
function contains_this(node) {
|
|
||||||
var result;
|
|
||||||
var tw = new TreeWalker(function(node) {
|
|
||||||
if (node instanceof AST_This) {
|
|
||||||
return result = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
node.walk(tw);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -313,8 +313,7 @@ issue_2105_1: {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
// TODO: outer function should be an arrow function
|
(() => {
|
||||||
(function() {
|
|
||||||
var quux = () => {
|
var quux = () => {
|
||||||
console.log("PASS");
|
console.log("PASS");
|
||||||
};
|
};
|
||||||
@@ -541,3 +540,60 @@ issue_2084: {
|
|||||||
expect_stdout: "0"
|
expect_stdout: "0"
|
||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export_default_object_expression: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
export default {
|
||||||
|
foo: 1 + 2,
|
||||||
|
bar() { return 4; },
|
||||||
|
get baz() { return this.foo; },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
expect_exact: "export default{foo:3,bar:()=>4,get baz(){return this.foo}};"
|
||||||
|
}
|
||||||
|
|
||||||
|
concise_methods_with_computed_property2: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var foo = {
|
||||||
|
[[1]](v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
console.log(foo[[1]]("PASS"));
|
||||||
|
}
|
||||||
|
expect_exact: 'var foo={[[1]]:v=>v};console.log(foo[[1]]("PASS"));'
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
async_object_literal: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
ecma: 6,
|
||||||
|
evaluate: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var obj = {
|
||||||
|
async a() {
|
||||||
|
return await foo(1 + 0);
|
||||||
|
},
|
||||||
|
anon: async function() {
|
||||||
|
return await foo(2 + 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var obj = {
|
||||||
|
a: async () => await foo(1),
|
||||||
|
anon: async () => await foo(2)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -561,6 +561,32 @@ prop_arrow_to_concise_method: {
|
|||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
concise_method_to_prop_arrow: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
ecma: 6,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(({ a: () => 1 }).a());
|
||||||
|
console.log(({ a: () => { return 2; } }).a());
|
||||||
|
console.log(({ a() { return 3; } }).a());
|
||||||
|
console.log(({ a() { return this.b; }, b: 4 }).a());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log({ a: () => 1 }.a());
|
||||||
|
console.log({ a: () => 2 }.a());
|
||||||
|
console.log({ a: () => 3 }.a());
|
||||||
|
console.log({ a() { return this.b; }, b: 4 }.a());
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
"3",
|
||||||
|
"4",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
prop_func_to_async_concise_method: {
|
prop_func_to_async_concise_method: {
|
||||||
options = {
|
options = {
|
||||||
ecma: 8,
|
ecma: 8,
|
||||||
@@ -648,19 +674,71 @@ prop_arrow_with_this: {
|
|||||||
ecma: 6,
|
ecma: 6,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
({
|
function run(arg) {
|
||||||
|
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
|
||||||
|
}
|
||||||
|
var foo = {
|
||||||
func_no_this: function() { run(); },
|
func_no_this: function() { run(); },
|
||||||
func_with_this: function() { run(this); },
|
func_with_this: function() { run(this); },
|
||||||
arrow_no_this: () => { run(); },
|
arrow_no_this: () => { run(); },
|
||||||
arrow_with_this: () => { run(this); },
|
arrow_with_this: () => { run(this); },
|
||||||
});
|
};
|
||||||
|
for (var key in foo) foo[key]();
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
({
|
function run(arg) {
|
||||||
|
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
|
||||||
|
}
|
||||||
|
var foo = {
|
||||||
func_no_this() { run(); },
|
func_no_this() { run(); },
|
||||||
func_with_this() { run(this); },
|
func_with_this() { run(this); },
|
||||||
arrow_no_this() { run(); },
|
arrow_no_this() { run(); },
|
||||||
arrow_with_this: () => { run(this); },
|
arrow_with_this: () => { run(this); },
|
||||||
});
|
};
|
||||||
|
for (var key in foo) foo[key]();
|
||||||
}
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"undefined",
|
||||||
|
"foo",
|
||||||
|
"undefined",
|
||||||
|
"global",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
prop_arrow_with_nested_this: {
|
||||||
|
options = {
|
||||||
|
ecma: 6,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function run(arg) {
|
||||||
|
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
|
||||||
|
}
|
||||||
|
var foo = {
|
||||||
|
func_func_this: function() { (function() { run(this); })(); },
|
||||||
|
func_arrow_this: function() { (() => { run(this); })(); },
|
||||||
|
arrow_func_this: () => { (function() { run(this); })(); },
|
||||||
|
arrow_arrow_this: () => { (() => { run(this); })(); },
|
||||||
|
};
|
||||||
|
for (var key in foo) foo[key]();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function run(arg) {
|
||||||
|
console.log(arg === this ? "global" : arg === foo ? "foo" : arg);
|
||||||
|
}
|
||||||
|
var foo = {
|
||||||
|
func_func_this() { (function() { run(this); })(); },
|
||||||
|
func_arrow_this() { (() => { run(this); })(); },
|
||||||
|
arrow_func_this() { (function() { run(this); })(); },
|
||||||
|
arrow_arrow_this: () => { (() => { run(this); })(); },
|
||||||
|
};
|
||||||
|
for (var key in foo) foo[key]();
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"global",
|
||||||
|
"foo",
|
||||||
|
"global",
|
||||||
|
"global",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ var assert = require("assert");
|
|||||||
describe("Object", function() {
|
describe("Object", function() {
|
||||||
it("Should allow objects to have a methodDefinition as property", function() {
|
it("Should allow objects to have a methodDefinition as property", function() {
|
||||||
var code = "var a = {test() {return true;}}";
|
var code = "var a = {test() {return true;}}";
|
||||||
assert.equal(Uglify.minify(code).code, "var a={test(){return!0}};");
|
assert.equal(Uglify.minify(code, {
|
||||||
|
compress: {
|
||||||
|
arrows: false
|
||||||
|
}
|
||||||
|
}).code, "var a={test(){return!0}};");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not allow objects to use static keywords like in classes", function() {
|
it("Should not allow objects to use static keywords like in classes", function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user