Parse and compress destructuring VarDefs

This commit is contained in:
Fábio Santos
2015-08-14 00:20:21 +01:00
committed by Richard van Velzen
parent 824ecfb8a2
commit c44c2d6c21
7 changed files with 105 additions and 8 deletions

View File

@@ -665,9 +665,12 @@ var AST_Const = DEFNODE("Const", null, {
var AST_VarDef = DEFNODE("VarDef", "name value", { var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node", $documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: { $propdoc: {
name: "[AST_SymbolVar|AST_SymbolConst] name of the variable", name: "[AST_SymbolVar|AST_SymbolConst|AST_Destructuring] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer" value: "[AST_Node?] initializer, or null of there's no initializer"
}, },
is_destructuring: function() {
return this.name instanceof AST_Destructuring;
},
_walk: function(visitor) { _walk: function(visitor) {
return visitor._visit(this, function(){ return visitor._visit(this, function(){
this.name._walk(visitor); this.name._walk(visitor);

View File

@@ -1056,6 +1056,7 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_Definitions && scope === self) { if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
if (def.is_destructuring()) return; /* Destructurings are type assertions! */
if (def.value) { if (def.value) {
initializations.add(def.name.name, def.value); initializations.add(def.name.name, def.value);
if (def.value.has_side_effects(compressor)) { if (def.value.has_side_effects(compressor)) {
@@ -1142,6 +1143,7 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) { if (node instanceof AST_Definitions && !(tt.parent() instanceof AST_ForIn)) {
var def = node.definitions.filter(function(def){ var def = node.definitions.filter(function(def){
if (def.is_destructuring()) return true;
if (member(def.name.definition(), in_use)) return true; if (member(def.name.definition(), in_use)) return true;
var w = { var w = {
name : def.name.name, name : def.name.name,
@@ -1262,6 +1264,7 @@ merge(Compressor.prototype, {
} }
if (node instanceof AST_Var && hoist_vars) { if (node instanceof AST_Var && hoist_vars) {
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
if (def.is_destructuring()) { return; }
vars.set(def.name.name, def); vars.set(def.name.name, def);
++vars_found; ++vars_found;
}); });
@@ -1702,13 +1705,23 @@ merge(Compressor.prototype, {
AST_Definitions.DEFMETHOD("to_assignments", function(){ AST_Definitions.DEFMETHOD("to_assignments", function(){
var assignments = this.definitions.reduce(function(a, def){ var assignments = this.definitions.reduce(function(a, def){
if (def.value) { if (def.value && !def.is_destructuring()) {
var name = make_node(AST_SymbolRef, def.name, def.name); var name = make_node(AST_SymbolRef, def.name, def.name);
a.push(make_node(AST_Assign, def, { a.push(make_node(AST_Assign, def, {
operator : "=", operator : "=",
left : name, left : name,
right : def.value right : def.value
})); }));
} else if (def.value) {
// Because it's a destructuring, do not turn into an assignment.
var varDef = make_node(AST_VarDef, def, {
name: def.name,
value: def.value
});
var var_ = make_node(AST_Var, def, {
definitions: [ varDef ]
});
a.push(var_);
} }
return a; return a;
}, []); }, []);

View File

@@ -1180,13 +1180,24 @@ function parse($TEXT, options) {
function vardefs(no_in, in_const) { function vardefs(no_in, in_const) {
var a = []; var a = [];
var def;
for (;;) { for (;;) {
a.push(new AST_VarDef({ if (is("punc", "{") || is("punc", "[")) {
def = new AST_VarDef({
start: S.token,
name: destructuring_(),
value: (expect_token("operator", "="), expression(false, no_in)),
end: prev()
});
} else {
def = new AST_VarDef({
start : S.token, start : S.token,
name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar), name : as_symbol(in_const ? AST_SymbolConst : AST_SymbolVar),
value : is("operator", "=") ? (next(), expression(false, no_in)) : null, value : is("operator", "=") ? (next(), expression(false, no_in)) : null,
end : prev() end : prev()
})); })
}
a.push(def);
if (!is("punc", ",")) if (!is("punc", ","))
break; break;
next(); next();
@@ -1194,6 +1205,16 @@ function parse($TEXT, options) {
return a; return a;
}; };
var destructuring_ = embed_tokens(function () {
var is_array = is("punc", "[");
var closing = is_array ? ']' : '}';
next()
return new AST_Destructuring({
names: expr_list(closing),
is_array: is_array
})
});
var var_ = function(no_in) { var var_ = function(no_in) {
return new AST_Var({ return new AST_Var({
start : prev(), start : prev(),

View File

@@ -0,0 +1,19 @@
destructuring_arrays: {
input: {
var [aa, bb] = cc;
}
expect: {
var[aa,bb]=cc;
}
}
destructuring_objects: {
input: {
var {aa, bb} = {aa:1, bb:2};
}
expect: {
var{aa,bb}={aa:1,bb:2};
}
}

View File

@@ -164,6 +164,21 @@ used_var_in_catch: {
} }
} }
unused_keep_harmony_destructuring: {
options = { unused: true };
input: {
function foo() {
var {x, y} = foo;
var a = foo;
}
}
expect: {
function foo() {
var {x, y} = foo;
}
}
}
keep_fnames: { keep_fnames: {
options = { unused: true, keep_fnames: true, unsafe: true }; options = { unused: true, keep_fnames: true, unsafe: true };
input: { input: {

View File

@@ -65,3 +65,21 @@ hoist_no_destructurings: {
} }
} }
} }
dont_hoist_var_destructurings: {
options = {
hoist_vars: true,
hoist_funs: true
}
input: {
function x() {
// If foo is null or undefined, this should be an exception
var {x,y} = foo;
}
}
expect: {
function x() {
var {x,y} = foo;
}
}
}

View File

@@ -94,6 +94,14 @@ module.exports = function () {
UglifyJS.parse('(function [a] { })'); UglifyJS.parse('(function [a] { })');
}); });
// Destructuring variable declaration
var decls = UglifyJS.parse('var {a,b} = foo, { c, d } = bar');
ok.equal(decls.body[0].TYPE, 'Var');
ok.equal(decls.body[0].definitions.length, 2);
ok.equal(decls.body[0].definitions[0].name.TYPE, 'Destructuring');
ok.throws(function () { ok.throws(function () {
// Note: this *is* a valid destructuring, but before we implement // Note: this *is* a valid destructuring, but before we implement
// destructuring (right now it's only destructuring *arguments*), // destructuring (right now it's only destructuring *arguments*),