support destructured literals (#4278)
This commit is contained in:
12
README.md
12
README.md
@@ -906,6 +906,8 @@ can pass additional arguments that control the code output:
|
|||||||
|
|
||||||
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
|
- `shebang` (default `true`) -- preserve shebang `#!` in preamble (bash scripts)
|
||||||
|
|
||||||
|
- `v8` (default `false`) -- enable workarounds for Chrome & Node.js bugs
|
||||||
|
|
||||||
- `webkit` (default `false`) -- enable workarounds for WebKit bugs.
|
- `webkit` (default `false`) -- enable workarounds for WebKit bugs.
|
||||||
PhantomJS users should set this option to `true`.
|
PhantomJS users should set this option to `true`.
|
||||||
|
|
||||||
@@ -1138,7 +1140,7 @@ To enable fast minify mode with the API use:
|
|||||||
UglifyJS.minify(code, { compress: false, mangle: true });
|
UglifyJS.minify(code, { compress: false, mangle: true });
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Source maps and debugging
|
### Source maps and debugging
|
||||||
|
|
||||||
Various `compress` transforms that simplify, rearrange, inline and remove code
|
Various `compress` transforms that simplify, rearrange, inline and remove code
|
||||||
are known to have an adverse effect on debugging with source maps. This is
|
are known to have an adverse effect on debugging with source maps. This is
|
||||||
@@ -1150,6 +1152,10 @@ disable the Uglify `compress` option and just use `mangle`.
|
|||||||
|
|
||||||
To allow for better optimizations, the compiler makes various assumptions:
|
To allow for better optimizations, the compiler makes various assumptions:
|
||||||
|
|
||||||
|
- The code does not rely on preserving its runtime performance characteristics.
|
||||||
|
Typically uglified code will run faster due to less instructions and easier
|
||||||
|
inlining, but may be slower on rare occasions for a specific platform, e.g.
|
||||||
|
see [`reduce_funcs`](#compress-options).
|
||||||
- `.toString()` and `.valueOf()` don't have side effects, and for built-in
|
- `.toString()` and `.valueOf()` don't have side effects, and for built-in
|
||||||
objects they have not been overridden.
|
objects they have not been overridden.
|
||||||
- `undefined`, `NaN` and `Infinity` have not been externally redefined.
|
- `undefined`, `NaN` and `Infinity` have not been externally redefined.
|
||||||
@@ -1177,3 +1183,7 @@ To allow for better optimizations, the compiler makes various assumptions:
|
|||||||
top.B = "PASS";
|
top.B = "PASS";
|
||||||
console.log(B);
|
console.log(B);
|
||||||
```
|
```
|
||||||
|
- Use of `arguments` alongside destructuring as function parameters, e.g.
|
||||||
|
`function({}, arguments) {}` will result in `SyntaxError` in earlier versions
|
||||||
|
of Chrome and Node.js - UglifyJS may modify the input which in turn may
|
||||||
|
suppress those errors.
|
||||||
|
|||||||
125
lib/ast.js
125
lib/ast.js
@@ -414,8 +414,12 @@ var AST_ForIn = DEFNODE("ForIn", "init object", {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.init instanceof AST_Definitions) {
|
if (this.init instanceof AST_Definitions) {
|
||||||
if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
|
if (this.init.definitions.length != 1) throw new Error("init must have single declaration");
|
||||||
} else if (!(this.init instanceof AST_PropAccess || this.init instanceof AST_SymbolRef)) {
|
} else {
|
||||||
throw new Error("init must be assignable");
|
validate_destructured(this.init, function(node) {
|
||||||
|
if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) {
|
||||||
|
throw new Error("init must be assignable: " + node.TYPE);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
must_be_expression(this, "object");
|
must_be_expression(this, "object");
|
||||||
},
|
},
|
||||||
@@ -496,12 +500,26 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
}
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
|
var AST_Lambda = DEFNODE("Lambda", "name argnames length_read uses_arguments", {
|
||||||
$documentation: "Base class for functions",
|
$documentation: "Base class for functions",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
name: "[AST_SymbolDeclaration?] the name of this function",
|
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||||
argnames: "[AST_SymbolFunarg*] array of function arguments",
|
argnames: "[(AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
|
||||||
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array"
|
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
|
||||||
|
},
|
||||||
|
each_argname: function(visit) {
|
||||||
|
var tw = new TreeWalker(function(node) {
|
||||||
|
if (node instanceof AST_DestructuredObject) {
|
||||||
|
node.properties.forEach(function(prop) {
|
||||||
|
prop.value.walk(tw);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_SymbolFunarg) visit(node);
|
||||||
|
});
|
||||||
|
this.argnames.forEach(function(argname) {
|
||||||
|
argname.walk(tw);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -515,7 +533,9 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
|
|||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
this.argnames.forEach(function(node) {
|
this.argnames.forEach(function(node) {
|
||||||
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
|
validate_destructured(node, function(node) {
|
||||||
|
if (!(node instanceof AST_SymbolFunarg)) throw new Error("argnames must be AST_SymbolFunarg[]");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
@@ -748,8 +768,10 @@ var AST_Const = DEFNODE("Const", null, {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
this.definitions.forEach(function(node) {
|
this.definitions.forEach(function(node) {
|
||||||
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
|
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
|
||||||
if (!(node.name instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst");
|
validate_destructured(node.name, function(node) {
|
||||||
must_be_expression(node, "value");
|
if (!(node instanceof AST_SymbolConst)) throw new Error("name must be AST_SymbolConst");
|
||||||
|
});
|
||||||
|
if (node.value != null) must_be_expression(node, "value");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}, AST_Definitions);
|
}, AST_Definitions);
|
||||||
@@ -759,7 +781,9 @@ var AST_Let = DEFNODE("Let", null, {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
this.definitions.forEach(function(node) {
|
this.definitions.forEach(function(node) {
|
||||||
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
|
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
|
||||||
if (!(node.name instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet");
|
validate_destructured(node.name, function(node) {
|
||||||
|
if (!(node instanceof AST_SymbolLet)) throw new Error("name must be AST_SymbolLet");
|
||||||
|
});
|
||||||
if (node.value != null) must_be_expression(node, "value");
|
if (node.value != null) must_be_expression(node, "value");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -770,7 +794,9 @@ var AST_Var = DEFNODE("Var", null, {
|
|||||||
_validate: function() {
|
_validate: function() {
|
||||||
this.definitions.forEach(function(node) {
|
this.definitions.forEach(function(node) {
|
||||||
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
|
if (!(node instanceof AST_VarDef)) throw new Error("definitions must be AST_VarDef[]");
|
||||||
if (!(node.name instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
|
validate_destructured(node.name, function(node) {
|
||||||
|
if (!(node instanceof AST_SymbolVar)) throw new Error("name must be AST_SymbolVar");
|
||||||
|
});
|
||||||
if (node.value != null) must_be_expression(node, "value");
|
if (node.value != null) must_be_expression(node, "value");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -969,6 +995,14 @@ var AST_Assign = DEFNODE("Assign", null, {
|
|||||||
$documentation: "An assignment expression — `a = b + 5`",
|
$documentation: "An assignment expression — `a = b + 5`",
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="');
|
if (this.operator.indexOf("=") < 0) throw new Error('operator must contain "="');
|
||||||
|
if (this.left instanceof AST_Destructured) {
|
||||||
|
if (this.operator != "=") throw new Error("invalid destructuring operator: " + this.operator);
|
||||||
|
validate_destructured(this.left, function(node) {
|
||||||
|
if (!(node instanceof AST_PropAccess || node instanceof AST_SymbolRef)) {
|
||||||
|
throw new Error("left must be assignable: " + node.TYPE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}, AST_Binary);
|
}, AST_Binary);
|
||||||
|
|
||||||
@@ -992,6 +1026,77 @@ var AST_Array = DEFNODE("Array", "elements", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var AST_Destructured = DEFNODE("Destructured", null, {
|
||||||
|
$documentation: "Base class for destructured literal",
|
||||||
|
});
|
||||||
|
|
||||||
|
function validate_destructured(node, check) {
|
||||||
|
if (node instanceof AST_DestructuredArray) return node.elements.forEach(function(node) {
|
||||||
|
if (!(node instanceof AST_Hole)) validate_destructured(node, check);
|
||||||
|
});
|
||||||
|
if (node instanceof AST_DestructuredObject) return node.properties.forEach(function(prop) {
|
||||||
|
validate_destructured(prop.value, check);
|
||||||
|
});
|
||||||
|
check(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
var AST_DestructuredArray = DEFNODE("DestructuredArray", "elements", {
|
||||||
|
$documentation: "A destructured array literal",
|
||||||
|
$propdoc: {
|
||||||
|
elements: "[AST_Node*] array of elements",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
node.elements.forEach(function(element) {
|
||||||
|
element.walk(visitor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, AST_Destructured);
|
||||||
|
|
||||||
|
var AST_DestructuredKeyVal = DEFNODE("DestructuredKeyVal", "key value", {
|
||||||
|
$documentation: "A key: value destructured property",
|
||||||
|
$propdoc: {
|
||||||
|
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
||||||
|
value: "[AST_Node] property value",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
if (node.key instanceof AST_Node) node.key.walk(visitor);
|
||||||
|
node.value.walk(visitor);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (typeof this.key != "string") {
|
||||||
|
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
|
||||||
|
must_be_expression(this, "key");
|
||||||
|
}
|
||||||
|
must_be_expression(this, "value");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var AST_DestructuredObject = DEFNODE("DestructuredObject", "properties", {
|
||||||
|
$documentation: "A destructured object literal",
|
||||||
|
$propdoc: {
|
||||||
|
properties: "[AST_DestructuredKeyVal*] array of properties",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
node.properties.forEach(function(prop) {
|
||||||
|
prop.walk(visitor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
this.properties.forEach(function(node) {
|
||||||
|
if (!(node instanceof AST_DestructuredKeyVal)) throw new Error("properties must be AST_DestructuredKeyVal[]");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, AST_Destructured);
|
||||||
|
|
||||||
var AST_Object = DEFNODE("Object", "properties", {
|
var AST_Object = DEFNODE("Object", "properties", {
|
||||||
$documentation: "An object literal",
|
$documentation: "An object literal",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
|
|||||||
800
lib/compress.js
800
lib/compress.js
File diff suppressed because it is too large
Load Diff
@@ -69,6 +69,7 @@ function OutputStream(options) {
|
|||||||
semicolons : true,
|
semicolons : true,
|
||||||
shebang : true,
|
shebang : true,
|
||||||
source_map : null,
|
source_map : null,
|
||||||
|
v8 : false,
|
||||||
webkit : false,
|
webkit : false,
|
||||||
width : 80,
|
width : 80,
|
||||||
wrap_iife : false,
|
wrap_iife : false,
|
||||||
@@ -499,11 +500,11 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (/comment[134]/.test(c.type)) {
|
if (/comment[134]/.test(c.type)) {
|
||||||
print("//" + c.value.replace(/[@#]__PURE__/g, ' ') + "\n");
|
print("//" + c.value.replace(/[@#]__PURE__/g, " ") + "\n");
|
||||||
indent();
|
indent();
|
||||||
last_nlb = true;
|
last_nlb = true;
|
||||||
} else if (c.type == "comment2") {
|
} else if (c.type == "comment2") {
|
||||||
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
|
print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
|
||||||
last_nlb = false;
|
last_nlb = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -557,10 +558,10 @@ function OutputStream(options) {
|
|||||||
space();
|
space();
|
||||||
}
|
}
|
||||||
if (/comment[134]/.test(c.type)) {
|
if (/comment[134]/.test(c.type)) {
|
||||||
print("//" + c.value.replace(/[@#]__PURE__/g, ' '));
|
print("//" + c.value.replace(/[@#]__PURE__/g, " "));
|
||||||
need_newline_indented = true;
|
need_newline_indented = true;
|
||||||
} else if (c.type == "comment2") {
|
} else if (c.type == "comment2") {
|
||||||
print("/*" + c.value.replace(/[@#]__PURE__/g, ' ') + "*/");
|
print("/*" + c.value.replace(/[@#]__PURE__/g, " ") + "*/");
|
||||||
need_space = true;
|
need_space = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -610,7 +611,7 @@ function OutputStream(options) {
|
|||||||
},
|
},
|
||||||
parent : function(n) {
|
parent : function(n) {
|
||||||
return stack[stack.length - 2 - (n || 0)];
|
return stack[stack.length - 2 - (n || 0)];
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,13 +653,7 @@ function OutputStream(options) {
|
|||||||
/* -----[ PARENTHESES ]----- */
|
/* -----[ PARENTHESES ]----- */
|
||||||
|
|
||||||
function PARENS(nodetype, func) {
|
function PARENS(nodetype, func) {
|
||||||
if (Array.isArray(nodetype)) {
|
nodetype.DEFMETHOD("needs_parens", func);
|
||||||
nodetype.forEach(function(nodetype) {
|
|
||||||
PARENS(nodetype, func);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
nodetype.DEFMETHOD("needs_parens", func);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PARENS(AST_Node, return_false);
|
PARENS(AST_Node, return_false);
|
||||||
@@ -667,11 +662,11 @@ function OutputStream(options) {
|
|||||||
// the first token to appear in a statement.
|
// the first token to appear in a statement.
|
||||||
PARENS(AST_Function, function(output) {
|
PARENS(AST_Function, function(output) {
|
||||||
if (!output.has_parens() && first_in_statement(output)) return true;
|
if (!output.has_parens() && first_in_statement(output)) return true;
|
||||||
if (output.option('webkit')) {
|
if (output.option("webkit")) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_PropAccess && p.expression === this) return true;
|
if (p instanceof AST_PropAccess && p.expression === this) return true;
|
||||||
}
|
}
|
||||||
if (output.option('wrap_iife')) {
|
if (output.option("wrap_iife")) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_Call && p.expression === this) return true;
|
if (p instanceof AST_Call && p.expression === this) return true;
|
||||||
}
|
}
|
||||||
@@ -679,9 +674,10 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
// same goes for an object literal, because otherwise it would be
|
// same goes for an object literal, because otherwise it would be
|
||||||
// interpreted as a block of code.
|
// interpreted as a block of code.
|
||||||
PARENS(AST_Object, function(output) {
|
function needs_parens_obj(output) {
|
||||||
return !output.has_parens() && first_in_statement(output);
|
return !output.has_parens() && first_in_statement(output);
|
||||||
});
|
}
|
||||||
|
PARENS(AST_Object, needs_parens_obj);
|
||||||
|
|
||||||
PARENS(AST_Unary, function(output) {
|
PARENS(AST_Unary, function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
@@ -701,6 +697,7 @@ function OutputStream(options) {
|
|||||||
|| p instanceof AST_Conditional
|
|| p instanceof AST_Conditional
|
||||||
// { [(1, 2)]: 3 }[2] ==> 3
|
// { [(1, 2)]: 3 }[2] ==> 3
|
||||||
// { foo: (1, 2) }.foo ==> 2
|
// { foo: (1, 2) }.foo ==> 2
|
||||||
|
|| p instanceof AST_DestructuredKeyVal
|
||||||
|| p instanceof AST_ObjectProperty
|
|| p instanceof AST_ObjectProperty
|
||||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||||
|| p instanceof AST_PropAccess && p.expression === this
|
|| p instanceof AST_PropAccess && p.expression === this
|
||||||
@@ -747,7 +744,7 @@ function OutputStream(options) {
|
|||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_New) return p.expression === this;
|
if (p instanceof AST_New) return p.expression === this;
|
||||||
// https://bugs.webkit.org/show_bug.cgi?id=123506
|
// https://bugs.webkit.org/show_bug.cgi?id=123506
|
||||||
if (output.option('webkit')) {
|
if (output.option("webkit")) {
|
||||||
var g = output.parent(1);
|
var g = output.parent(1);
|
||||||
return this.expression instanceof AST_Function
|
return this.expression instanceof AST_Function
|
||||||
&& p instanceof AST_PropAccess
|
&& p instanceof AST_PropAccess
|
||||||
@@ -776,18 +773,29 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS([ AST_Assign, AST_Conditional ], function(output) {
|
function needs_parens_assign_cond(self, output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// 1 + (a = 2) + 3 → 6, side effect setting a = 2
|
// 1 + (a = 2) + 3 → 6, side effect setting a = 2
|
||||||
if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
|
if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
|
||||||
// (a = func)() —or— new (a = Object)()
|
// (a = func)() —or— new (a = Object)()
|
||||||
if (p instanceof AST_Call) return p.expression === this;
|
if (p instanceof AST_Call) return p.expression === self;
|
||||||
// (a = foo) ? bar : baz
|
// (a = foo) ? bar : baz
|
||||||
if (p instanceof AST_Conditional) return p.condition === this;
|
if (p instanceof AST_Conditional) return p.condition === self;
|
||||||
// (a = foo)["prop"] —or— (a = foo).prop
|
// (a = foo)["prop"] —or— (a = foo).prop
|
||||||
if (p instanceof AST_PropAccess) return p.expression === this;
|
if (p instanceof AST_PropAccess) return p.expression === self;
|
||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
if (p instanceof AST_Unary) return true;
|
if (p instanceof AST_Unary) return true;
|
||||||
|
}
|
||||||
|
PARENS(AST_Assign, function(output) {
|
||||||
|
if (needs_parens_assign_cond(this, output)) return true;
|
||||||
|
// v8 parser bug => workaround
|
||||||
|
// f([1], [a] = []) => f([1], ([a] = []))
|
||||||
|
if (output.option("v8")) return this.left instanceof AST_Destructured;
|
||||||
|
// ({ p: a } = o);
|
||||||
|
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
|
||||||
|
});
|
||||||
|
PARENS(AST_Conditional, function(output) {
|
||||||
|
return needs_parens_assign_cond(this, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ PRINTERS ]----- */
|
/* -----[ PRINTERS ]----- */
|
||||||
@@ -1274,6 +1282,38 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
} : noop);
|
} : noop);
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_DestructuredArray, function(output) {
|
||||||
|
var a = this.elements, len = a.length;
|
||||||
|
output.with_square(len > 0 ? function() {
|
||||||
|
output.space();
|
||||||
|
a.forEach(function(exp, i) {
|
||||||
|
if (i) output.comma();
|
||||||
|
exp.print(output);
|
||||||
|
// If the final element is a hole, we need to make sure it
|
||||||
|
// doesn't look like a trailing comma, by inserting an actual
|
||||||
|
// trailing comma.
|
||||||
|
if (i === len - 1 && exp instanceof AST_Hole)
|
||||||
|
output.comma();
|
||||||
|
});
|
||||||
|
output.space();
|
||||||
|
} : noop);
|
||||||
|
});
|
||||||
|
DEFPRINT(AST_DestructuredKeyVal, print_key_value);
|
||||||
|
DEFPRINT(AST_DestructuredObject, function(output) {
|
||||||
|
var props = this.properties;
|
||||||
|
if (props.length > 0) output.with_block(function() {
|
||||||
|
props.forEach(function(prop, i) {
|
||||||
|
if (i) {
|
||||||
|
output.print(",");
|
||||||
|
output.newline();
|
||||||
|
}
|
||||||
|
output.indent();
|
||||||
|
prop.print(output);
|
||||||
|
});
|
||||||
|
output.newline();
|
||||||
|
});
|
||||||
|
else print_braced_empty(this, output);
|
||||||
|
});
|
||||||
DEFPRINT(AST_Object, function(output) {
|
DEFPRINT(AST_Object, function(output) {
|
||||||
var props = this.properties;
|
var props = this.properties;
|
||||||
if (props.length > 0) output.with_block(function() {
|
if (props.length > 0) output.with_block(function() {
|
||||||
@@ -1314,12 +1354,13 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPRINT(AST_ObjectKeyVal, function(output) {
|
function print_key_value(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
print_property_key(self, output);
|
print_property_key(self, output);
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
});
|
}
|
||||||
|
DEFPRINT(AST_ObjectKeyVal, print_key_value);
|
||||||
function print_accessor(type) {
|
function print_accessor(type) {
|
||||||
return function(output) {
|
return function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
@@ -1483,6 +1524,7 @@ function OutputStream(options) {
|
|||||||
AST_Constant,
|
AST_Constant,
|
||||||
AST_Debugger,
|
AST_Debugger,
|
||||||
AST_Definitions,
|
AST_Definitions,
|
||||||
|
AST_Destructured,
|
||||||
AST_Finally,
|
AST_Finally,
|
||||||
AST_Jump,
|
AST_Jump,
|
||||||
AST_Lambda,
|
AST_Lambda,
|
||||||
@@ -1497,7 +1539,7 @@ function OutputStream(options) {
|
|||||||
output.add_mapping(this.start);
|
output.add_mapping(this.start);
|
||||||
});
|
});
|
||||||
|
|
||||||
DEFMAP([ AST_ObjectProperty ], function(output) {
|
DEFMAP([ AST_DestructuredKeyVal, AST_ObjectProperty ], function(output) {
|
||||||
if (typeof this.key == "string") output.add_mapping(this.start, this.key);
|
if (typeof this.key == "string") output.add_mapping(this.start, this.key);
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
103
lib/parse.js
103
lib/parse.js
@@ -973,14 +973,16 @@ function parse($TEXT, options) {
|
|||||||
if (!is("punc", ";")) {
|
if (!is("punc", ";")) {
|
||||||
init = is("keyword", "const")
|
init = is("keyword", "const")
|
||||||
? (next(), const_(true))
|
? (next(), const_(true))
|
||||||
|
: is("keyword", "let")
|
||||||
|
? (next(), let_(true))
|
||||||
: is("keyword", "var")
|
: is("keyword", "var")
|
||||||
? (next(), var_(true))
|
? (next(), var_(true))
|
||||||
: expression(true, true);
|
: expression(true, true);
|
||||||
if (is("operator", "in")) {
|
if (is("operator", "in")) {
|
||||||
if (init instanceof AST_Var) {
|
if (init instanceof AST_Definitions) {
|
||||||
if (init.definitions.length > 1)
|
if (init.definitions.length > 1)
|
||||||
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
|
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||||
} else if (!is_assignable(init)) {
|
} else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
|
||||||
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
|
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
@@ -1025,7 +1027,7 @@ function parse($TEXT, options) {
|
|||||||
var argnames = [];
|
var argnames = [];
|
||||||
for (var first = true; !is("punc", ")");) {
|
for (var first = true; !is("punc", ")");) {
|
||||||
if (first) first = false; else expect(",");
|
if (first) first = false; else expect(",");
|
||||||
argnames.push(as_symbol(AST_SymbolFunarg));
|
argnames.push(maybe_destructured(AST_SymbolFunarg));
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
var loop = S.in_loop;
|
var loop = S.in_loop;
|
||||||
@@ -1146,16 +1148,16 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function vardefs(type, no_in, must_init) {
|
function vardefs(type, no_in) {
|
||||||
var a = [];
|
var a = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var name = as_symbol(type);
|
var name = maybe_destructured(type);
|
||||||
var value = null;
|
var value = null;
|
||||||
if (is("operator", "=")) {
|
if (is("operator", "=")) {
|
||||||
next();
|
next();
|
||||||
value = expression(false, no_in);
|
value = expression(false, no_in);
|
||||||
} else if (must_init) {
|
} else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
|
||||||
croak("Missing initializer in declaration");
|
croak("Missing initializer in declaration");
|
||||||
}
|
}
|
||||||
a.push(new AST_VarDef({
|
a.push(new AST_VarDef({
|
||||||
@@ -1174,7 +1176,7 @@ function parse($TEXT, options) {
|
|||||||
var const_ = function(no_in) {
|
var const_ = function(no_in) {
|
||||||
return new AST_Const({
|
return new AST_Const({
|
||||||
start : prev(),
|
start : prev(),
|
||||||
definitions : vardefs(AST_SymbolConst, no_in, true),
|
definitions : vardefs(AST_SymbolConst, no_in),
|
||||||
end : prev()
|
end : prev()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -1306,7 +1308,8 @@ function parse($TEXT, options) {
|
|||||||
unexpected();
|
unexpected();
|
||||||
};
|
};
|
||||||
|
|
||||||
function expr_list(closing, allow_trailing_comma, allow_empty) {
|
function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
|
||||||
|
if (!parser) parser = expression;
|
||||||
var first = true, a = [];
|
var first = true, a = [];
|
||||||
while (!is("punc", closing)) {
|
while (!is("punc", closing)) {
|
||||||
if (first) first = false; else expect(",");
|
if (first) first = false; else expect(",");
|
||||||
@@ -1314,7 +1317,7 @@ function parse($TEXT, options) {
|
|||||||
if (is("punc", ",") && allow_empty) {
|
if (is("punc", ",") && allow_empty) {
|
||||||
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||||
} else {
|
} else {
|
||||||
a.push(expression(false));
|
a.push(parser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
@@ -1449,6 +1452,54 @@ function parse($TEXT, options) {
|
|||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maybe_destructured(type) {
|
||||||
|
var start = S.token;
|
||||||
|
if (is("punc", "[")) {
|
||||||
|
next();
|
||||||
|
return new AST_DestructuredArray({
|
||||||
|
start: start,
|
||||||
|
elements: expr_list("]", !options.strict, true, function() {
|
||||||
|
return maybe_destructured(type);
|
||||||
|
}),
|
||||||
|
end: prev(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (is("punc", "{")) {
|
||||||
|
next();
|
||||||
|
var first = true, a = [];
|
||||||
|
while (!is("punc", "}")) {
|
||||||
|
if (first) first = false; else expect(",");
|
||||||
|
// allow trailing comma
|
||||||
|
if (!options.strict && is("punc", "}")) break;
|
||||||
|
var key_start = S.token;
|
||||||
|
var key = as_property_key();
|
||||||
|
if (!is("punc", ":") && key_start.type == "name") {
|
||||||
|
a.push(new AST_DestructuredKeyVal({
|
||||||
|
start: key_start,
|
||||||
|
key: key,
|
||||||
|
value: _make_symbol(type, key_start),
|
||||||
|
end: prev(),
|
||||||
|
}));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
expect(":");
|
||||||
|
a.push(new AST_DestructuredKeyVal({
|
||||||
|
start: key_start,
|
||||||
|
key: key,
|
||||||
|
value: maybe_destructured(type),
|
||||||
|
end: prev(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
return new AST_DestructuredObject({
|
||||||
|
start: start,
|
||||||
|
properties: a,
|
||||||
|
end: prev(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return as_symbol(type);
|
||||||
|
}
|
||||||
|
|
||||||
function mark_pure(call) {
|
function mark_pure(call) {
|
||||||
var start = call.start;
|
var start = call.start;
|
||||||
var comments = start.comments_before;
|
var comments = start.comments_before;
|
||||||
@@ -1578,11 +1629,43 @@ function parse($TEXT, options) {
|
|||||||
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
|
return expr instanceof AST_PropAccess || expr instanceof AST_SymbolRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function to_destructured(node) {
|
||||||
|
if (node instanceof AST_Array) {
|
||||||
|
var elements = node.elements.map(to_destructured);
|
||||||
|
return all(elements, function(node) {
|
||||||
|
return node instanceof AST_Destructured || node instanceof AST_Hole || is_assignable(node);
|
||||||
|
}) ? new AST_DestructuredArray({
|
||||||
|
start: node.start,
|
||||||
|
elements: elements,
|
||||||
|
end: node.end,
|
||||||
|
}) : node;
|
||||||
|
}
|
||||||
|
if (!(node instanceof AST_Object)) return node;
|
||||||
|
var props = [];
|
||||||
|
for (var i = 0; i < node.properties.length; i++) {
|
||||||
|
var prop = node.properties[i];
|
||||||
|
if (!(prop instanceof AST_ObjectKeyVal)) return node;
|
||||||
|
var value = to_destructured(prop.value);
|
||||||
|
if (!(value instanceof AST_Destructured || is_assignable(value))) return node;
|
||||||
|
props.push(new AST_DestructuredKeyVal({
|
||||||
|
start: prop.start,
|
||||||
|
key: prop.key,
|
||||||
|
value: value,
|
||||||
|
end: prop.end,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return new AST_DestructuredObject({
|
||||||
|
start: node.start,
|
||||||
|
properties: props,
|
||||||
|
end: node.end,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var maybe_assign = function(no_in) {
|
var maybe_assign = function(no_in) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var left = maybe_conditional(no_in), val = S.token.value;
|
var left = maybe_conditional(no_in), val = S.token.value;
|
||||||
if (is("operator") && ASSIGNMENT[val]) {
|
if (is("operator") && ASSIGNMENT[val]) {
|
||||||
if (is_assignable(left)) {
|
if (is_assignable(left) || val == "=" && (left = to_destructured(left)) instanceof AST_Destructured) {
|
||||||
next();
|
next();
|
||||||
return new AST_Assign({
|
return new AST_Assign({
|
||||||
start : start,
|
start : start,
|
||||||
|
|||||||
20
lib/scope.js
20
lib/scope.js
@@ -204,7 +204,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
|
|
||||||
// pass 2: find back references and eval
|
// pass 2: find back references and eval
|
||||||
self.globals = new Dictionary();
|
self.globals = new Dictionary();
|
||||||
|
var in_arg = [];
|
||||||
var tw = new TreeWalker(function(node) {
|
var tw = new TreeWalker(function(node) {
|
||||||
|
if (node instanceof AST_Lambda) {
|
||||||
|
in_arg.push(node);
|
||||||
|
node.argnames.forEach(function(argname) {
|
||||||
|
argname.walk(tw);
|
||||||
|
});
|
||||||
|
in_arg.pop();
|
||||||
|
walk_body(node, tw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_LoopControl) {
|
if (node instanceof AST_LoopControl) {
|
||||||
if (node.label) node.label.thedef.references.push(node);
|
if (node.label) node.label.thedef.references.push(node);
|
||||||
return true;
|
return true;
|
||||||
@@ -212,6 +222,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
var name = node.name;
|
var name = node.name;
|
||||||
var sym = node.scope.find_variable(name);
|
var sym = node.scope.find_variable(name);
|
||||||
|
for (var i = in_arg.length; i > 0 && sym;) {
|
||||||
|
i = in_arg.lastIndexOf(sym.scope, i - 1);
|
||||||
|
if (i < 0) break;
|
||||||
|
var decl = sym.orig[0];
|
||||||
|
if (decl instanceof AST_SymbolFunarg || decl instanceof AST_SymbolLambda) {
|
||||||
|
node.in_arg = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sym = sym.scope.parent_scope.find_variable(name);
|
||||||
|
}
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
sym = self.def_global(node);
|
sym = self.def_global(node);
|
||||||
} else if (name == "arguments" && sym.scope instanceof AST_Lambda) {
|
} else if (name == "arguments" && sym.scope instanceof AST_Lambda) {
|
||||||
|
|||||||
@@ -160,6 +160,16 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
DEF(AST_Array, function(self, tw) {
|
DEF(AST_Array, function(self, tw) {
|
||||||
self.elements = do_list(self.elements, tw);
|
self.elements = do_list(self.elements, tw);
|
||||||
});
|
});
|
||||||
|
DEF(AST_DestructuredArray, function(self, tw) {
|
||||||
|
self.elements = do_list(self.elements, tw);
|
||||||
|
});
|
||||||
|
DEF(AST_DestructuredKeyVal, function(self, tw) {
|
||||||
|
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||||
|
self.value = self.value.transform(tw);
|
||||||
|
});
|
||||||
|
DEF(AST_DestructuredObject, function(self, tw) {
|
||||||
|
self.properties = do_list(self.properties, tw);
|
||||||
|
});
|
||||||
DEF(AST_Object, function(self, tw) {
|
DEF(AST_Object, function(self, tw) {
|
||||||
self.properties = do_list(self.properties, tw);
|
self.properties = do_list(self.properties, tw);
|
||||||
});
|
});
|
||||||
|
|||||||
1297
test/compress/destructured.js
Normal file
1297
test/compress/destructured.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -95,6 +95,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
|
|
||||||
// quick ignores
|
// quick ignores
|
||||||
if (node instanceof U.AST_Accessor) return;
|
if (node instanceof U.AST_Accessor) return;
|
||||||
|
if (node instanceof U.AST_Destructured) return;
|
||||||
if (node instanceof U.AST_Directive) return;
|
if (node instanceof U.AST_Directive) return;
|
||||||
if (!in_list && node instanceof U.AST_EmptyStatement) return;
|
if (!in_list && node instanceof U.AST_EmptyStatement) return;
|
||||||
if (node instanceof U.AST_Label) return;
|
if (node instanceof U.AST_Label) return;
|
||||||
@@ -114,6 +115,8 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
|
|
||||||
// ignore lvalues
|
// ignore lvalues
|
||||||
if (parent instanceof U.AST_Assign && parent.left === node) return;
|
if (parent instanceof U.AST_Assign && parent.left === node) return;
|
||||||
|
if (parent instanceof U.AST_Destructured) return;
|
||||||
|
if (parent instanceof U.AST_DestructuredKeyVal && parent.value === node) return;
|
||||||
if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
|
if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
|
||||||
case "++":
|
case "++":
|
||||||
case "--":
|
case "--":
|
||||||
@@ -250,13 +253,23 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (node instanceof U.AST_ForIn) {
|
else if (node instanceof U.AST_ForIn) {
|
||||||
var expr = [
|
var expr;
|
||||||
node.init,
|
switch ((node.start._permute * steps | 0) % 3) {
|
||||||
node.object,
|
case 0:
|
||||||
node.body,
|
if (!(node.init instanceof U.AST_Definitions
|
||||||
][ (node.start._permute * steps | 0) % 3 ];
|
&& node.init.definitions[0].name instanceof U.AST_Destructured)) {
|
||||||
|
expr = node.init;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
expr = node.object;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (!has_loopcontrol(node.body, node, parent)) expr = node.body;
|
||||||
|
break;
|
||||||
|
}
|
||||||
node.start._permute += step;
|
node.start._permute += step;
|
||||||
if (expr && (expr !== node.body || !has_loopcontrol(expr, node, parent))) {
|
if (expr) {
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return to_statement(expr);
|
return to_statement(expr);
|
||||||
}
|
}
|
||||||
@@ -389,6 +402,13 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return List.skip;
|
return List.skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip element/property from (destructured) array/object
|
||||||
|
if (parent instanceof U.AST_Array || parent instanceof U.AST_Destructured || parent instanceof AST_Object) {
|
||||||
|
node.start._permute++;
|
||||||
|
CHANGED = true;
|
||||||
|
return List.skip;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace this node
|
// replace this node
|
||||||
|
|||||||
@@ -377,6 +377,118 @@ function createArgs(recurmax, stmtDepth, canThrow) {
|
|||||||
return args.join(", ");
|
return args.join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, maybe, dontStore) {
|
||||||
|
var avoid = [];
|
||||||
|
var len = unique_vars.length;
|
||||||
|
var pairs = createPairs(recurmax);
|
||||||
|
unique_vars.length = len;
|
||||||
|
return pairs;
|
||||||
|
|
||||||
|
function createAssignmentValue(recurmax) {
|
||||||
|
var current = VAR_NAMES;
|
||||||
|
VAR_NAMES = (varNames || VAR_NAMES).slice();
|
||||||
|
var value = varNames && rng(2) ? createValue() : createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
|
VAR_NAMES = current;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createKey(recurmax, keys) {
|
||||||
|
var save = VAR_NAMES;
|
||||||
|
VAR_NAMES = VAR_NAMES.filter(function(name) {
|
||||||
|
return avoid.indexOf(name) < 0;
|
||||||
|
});
|
||||||
|
var len = VAR_NAMES.length;
|
||||||
|
var key;
|
||||||
|
do {
|
||||||
|
key = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
|
} while (keys.indexOf(key) >= 0);
|
||||||
|
VAR_NAMES = save.concat(VAR_NAMES.slice(len));
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPairs(recurmax) {
|
||||||
|
var names = [], values = [];
|
||||||
|
var m = rng(4), n = rng(4);
|
||||||
|
if (!varNames) m = Math.max(m, n, 1);
|
||||||
|
for (var i = Math.max(m, n); --i >= 0;) {
|
||||||
|
if (i < m && i < n) {
|
||||||
|
createDestructured(recurmax, names, values);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i < m) {
|
||||||
|
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||||
|
var name = createVarName(maybe, dontStore);
|
||||||
|
unique_vars.length -= 6;
|
||||||
|
avoid.push(name);
|
||||||
|
unique_vars.push(name);
|
||||||
|
names.unshift(name);
|
||||||
|
}
|
||||||
|
if (i < n) {
|
||||||
|
values.unshift(createAssignmentValue(recurmax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
names: names,
|
||||||
|
values: values,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDestructured(recurmax, names, values) {
|
||||||
|
switch (rng(20)) {
|
||||||
|
case 0:
|
||||||
|
if (--recurmax < 0) {
|
||||||
|
names.unshift("[]");
|
||||||
|
values.unshift('""');
|
||||||
|
} else {
|
||||||
|
var pairs = createPairs(recurmax);
|
||||||
|
while (!rng(10)) {
|
||||||
|
var index = rng(pairs.names.length + 1);
|
||||||
|
pairs.names.splice(index, 0, "");
|
||||||
|
pairs.values.splice(index, 0, rng(2) ? createAssignmentValue(recurmax) : "");
|
||||||
|
}
|
||||||
|
names.unshift("[ " + pairs.names.join(", ") + " ]");
|
||||||
|
values.unshift("[ " + pairs.values.join(", ") + " ]");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (--recurmax < 0) {
|
||||||
|
names.unshift("{}");
|
||||||
|
values.unshift('""');
|
||||||
|
} else {
|
||||||
|
var pairs = createPairs(recurmax);
|
||||||
|
var keys = [];
|
||||||
|
pairs.names.forEach(function(name, index) {
|
||||||
|
if (/^[[{]/.test(name)) {
|
||||||
|
var key;
|
||||||
|
do {
|
||||||
|
key = KEYS[rng(KEYS.length)];
|
||||||
|
} while (keys.indexOf(key) >= 0);
|
||||||
|
keys[index] = key;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
names.unshift("{ " + pairs.names.map(function(name, index) {
|
||||||
|
var key = index in keys ? keys[index] : rng(10) && createKey(recurmax, keys);
|
||||||
|
return key ? key + ": " + name : name;
|
||||||
|
}).join(", ") + " }");
|
||||||
|
values.unshift("{ " + pairs.values.map(function(value, index) {
|
||||||
|
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
||||||
|
return key + ": " + value;
|
||||||
|
}).join(", ") + " }");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||||
|
var name = createVarName(maybe, dontStore);
|
||||||
|
unique_vars.length -= 6;
|
||||||
|
avoid.push(name);
|
||||||
|
unique_vars.push(name);
|
||||||
|
names.unshift(name);
|
||||||
|
values.unshift(createAssignmentValue(recurmax));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function filterDirective(s) {
|
function filterDirective(s) {
|
||||||
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2];
|
if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2];
|
||||||
return s;
|
return s;
|
||||||
@@ -415,11 +527,37 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
|||||||
return names.indexOf(name) < 0;
|
return names.indexOf(name) < 0;
|
||||||
});
|
});
|
||||||
var len = VAR_NAMES.length;
|
var len = VAR_NAMES.length;
|
||||||
var s = type + " " + names.map(function(name) {
|
var s = type + " ";
|
||||||
var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
|
switch (rng(10)) {
|
||||||
VAR_NAMES.push(name);
|
case 0:
|
||||||
return name + " = " + value;
|
while (!rng(10)) names.splice(rng(names.length + 1), 0, "");
|
||||||
}).join(", ") + ";";
|
s += "[ " + names.join(", ") + " ] = [ " + names.map(function() {
|
||||||
|
return rng(10) ? createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) : "";
|
||||||
|
}).join(", ") + " ];";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
var keys = [];
|
||||||
|
s += "{ " + names.map(function(name, i) {
|
||||||
|
var key = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
|
if (!/\[/.test(key)) keys[i] = key;
|
||||||
|
return key + ": " + name;
|
||||||
|
}).join(", ") + "} = { " + names.map(function(name, i) {
|
||||||
|
var key = i in keys ? keys[i] : createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
|
return key + ": " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
|
||||||
|
}).join(", ") + "};";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s += names.map(function(name, i) {
|
||||||
|
if (type == "let" && !rng(10)) {
|
||||||
|
VAR_NAMES.push(name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
|
||||||
|
VAR_NAMES.push(name);
|
||||||
|
return name + " = " + value;
|
||||||
|
}).join(", ") + ";";
|
||||||
|
break;
|
||||||
|
}
|
||||||
VAR_NAMES = save.concat(VAR_NAMES.slice(len));
|
VAR_NAMES = save.concat(VAR_NAMES.slice(len));
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -429,9 +567,9 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
if (--recurmax < 0) { return ";"; }
|
if (--recurmax < 0) { return ";"; }
|
||||||
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
||||||
var s = [];
|
var s = [];
|
||||||
var name;
|
var name, args;
|
||||||
|
var varNames = VAR_NAMES.slice();
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
var namesLenBefore = VAR_NAMES.length;
|
|
||||||
if (allowDefun || rng(5) > 0) {
|
if (allowDefun || rng(5) > 0) {
|
||||||
name = "f" + funcs++;
|
name = "f" + funcs++;
|
||||||
} else {
|
} else {
|
||||||
@@ -439,7 +577,16 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
name = createVarName(MANDATORY, !allowDefun);
|
name = createVarName(MANDATORY, !allowDefun);
|
||||||
unique_vars.length -= 3;
|
unique_vars.length -= 3;
|
||||||
}
|
}
|
||||||
s.push("function " + name + "(" + createParams() + "){", strictMode());
|
var params;
|
||||||
|
if ((!allowDefun || !(name in called)) && rng(2)) {
|
||||||
|
called[name] = false;
|
||||||
|
var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, MANDATORY);
|
||||||
|
params = pairs.names.join(", ");
|
||||||
|
args = pairs.values.join(", ");
|
||||||
|
} else {
|
||||||
|
params = createParams();
|
||||||
|
}
|
||||||
|
s.push("function " + name + "(" + params + "){", strictMode());
|
||||||
s.push(defns());
|
s.push(defns());
|
||||||
if (rng(5) === 0) {
|
if (rng(5) === 0) {
|
||||||
// functions with functions. lower the recursion to prevent a mess.
|
// functions with functions. lower the recursion to prevent a mess.
|
||||||
@@ -450,17 +597,16 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
}
|
}
|
||||||
s.push("}", "");
|
s.push("}", "");
|
||||||
s = filterDirective(s).join("\n");
|
s = filterDirective(s).join("\n");
|
||||||
|
|
||||||
VAR_NAMES.length = namesLenBefore;
|
|
||||||
});
|
});
|
||||||
|
VAR_NAMES = varNames;
|
||||||
|
|
||||||
if (!allowDefun) {
|
if (!allowDefun) {
|
||||||
// avoid "function statements" (decl inside statements)
|
// avoid "function statements" (decl inside statements)
|
||||||
s = "var " + createVarName(MANDATORY) + " = " + s;
|
s = "var " + createVarName(MANDATORY) + " = " + s;
|
||||||
s += "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
|
||||||
} else if (!(name in called) || rng(3) > 0) {
|
} else if (!(name in called) || args || rng(3)) {
|
||||||
s += "var " + createVarName(MANDATORY) + " = " + name;
|
s += "var " + createVarName(MANDATORY) + " = " + name;
|
||||||
s += "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
return s + ";";
|
return s + ";";
|
||||||
@@ -561,8 +707,9 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
return [
|
return [
|
||||||
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
|
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
|
||||||
label.target + " for (",
|
label.target + " for (",
|
||||||
/^key/.test(key) ? "var " : "",
|
!/^key/.test(key) ? rng(10) ? "" : "var " : rng(10) ? "var " : rng(2) ? "let " : "const ",
|
||||||
key + " in expr" + loop + ") {",
|
rng(20) ? key : "{ length: " + key + " }",
|
||||||
|
" in expr" + loop + ") {",
|
||||||
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
|
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
|
||||||
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
|
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
|
||||||
"}}",
|
"}}",
|
||||||
@@ -576,7 +723,12 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
// note: default does not _need_ to be last
|
// note: default does not _need_ to be last
|
||||||
return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
|
return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
|
||||||
case STMT_VAR:
|
case STMT_VAR:
|
||||||
switch (rng(3)) {
|
if (!rng(20)) {
|
||||||
|
var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow, null, MANDATORY);
|
||||||
|
return "var " + pairs.names.map(function(name, index) {
|
||||||
|
return index in pairs.values ? name + " = " + pairs.values[index] : name;
|
||||||
|
}).join(", ") + ";";
|
||||||
|
} else switch (rng(3)) {
|
||||||
case 0:
|
case 0:
|
||||||
unique_vars.push("c");
|
unique_vars.push("c");
|
||||||
var name = createVarName(MANDATORY);
|
var name = createVarName(MANDATORY);
|
||||||
@@ -719,7 +871,28 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
case p++:
|
case p++:
|
||||||
return getVarName();
|
return getVarName();
|
||||||
case p++:
|
case p++:
|
||||||
return getVarName(NO_CONST) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
|
switch (rng(20)) {
|
||||||
|
case 0:
|
||||||
|
return [
|
||||||
|
"[ ",
|
||||||
|
new Array(rng(3)).join(","),
|
||||||
|
getVarName(NO_CONST),
|
||||||
|
new Array(rng(3)).join(","),
|
||||||
|
" ] = ",
|
||||||
|
createArrayLiteral(recurmax, stmtDepth, canThrow),
|
||||||
|
].join("");
|
||||||
|
case 1:
|
||||||
|
return [
|
||||||
|
"{ ",
|
||||||
|
rng(2) ? "" : createObjectKey(recurmax, stmtDepth, canThrow) + ": ",
|
||||||
|
getVarName(NO_CONST),
|
||||||
|
" } = ",
|
||||||
|
createExpression(recurmax, COMMA_OK, stmtDepth, canThrow),
|
||||||
|
" || {}",
|
||||||
|
].join("");
|
||||||
|
default:
|
||||||
|
return getVarName(NO_CONST) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
|
||||||
|
}
|
||||||
case p++:
|
case p++:
|
||||||
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
|
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
|
||||||
case p++:
|
case p++:
|
||||||
@@ -864,7 +1037,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
var name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
|
var name;
|
||||||
|
do {
|
||||||
|
name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
|
||||||
|
} while (name in called && !called[name]);
|
||||||
called[name] = true;
|
called[name] = true;
|
||||||
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
||||||
}
|
}
|
||||||
@@ -1002,13 +1178,77 @@ function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
|
return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
|
||||||
case 3:
|
case 3:
|
||||||
assignee = getVarName();
|
assignee = getVarName();
|
||||||
expr = "(" + assignee + "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow)
|
switch (rng(20)) {
|
||||||
+ "]" + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
|
case 0:
|
||||||
|
expr = [
|
||||||
|
"([ ",
|
||||||
|
assignee,
|
||||||
|
"[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
|
||||||
|
" ] = [ ",
|
||||||
|
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
|
||||||
|
" ])",
|
||||||
|
].join("");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
|
var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
|
||||||
|
expr = [
|
||||||
|
"({ ",
|
||||||
|
key1, ": ", assignee,
|
||||||
|
"[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
|
||||||
|
" } = { ",
|
||||||
|
key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
|
||||||
|
" })",
|
||||||
|
].join("");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
expr = [
|
||||||
|
"(",
|
||||||
|
assignee,
|
||||||
|
"[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
|
||||||
|
createAssignment(),
|
||||||
|
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
|
||||||
|
")",
|
||||||
|
].join("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")";
|
return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")";
|
||||||
case 4:
|
case 4:
|
||||||
assignee = getVarName();
|
assignee = getVarName();
|
||||||
expr = "(" + assignee + "." + getDotKey(true) + createAssignment()
|
switch (rng(20)) {
|
||||||
+ _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
|
case 0:
|
||||||
|
expr = [
|
||||||
|
"([ ",
|
||||||
|
assignee,
|
||||||
|
".", getDotKey(true),
|
||||||
|
" ] = [ ",
|
||||||
|
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
|
||||||
|
" ])",
|
||||||
|
].join("");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
|
var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
|
||||||
|
expr = [
|
||||||
|
"({ ",
|
||||||
|
key1, ": ", assignee,
|
||||||
|
".", getDotKey(true),
|
||||||
|
" } = { ",
|
||||||
|
key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
|
||||||
|
" })",
|
||||||
|
].join("");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
expr = [
|
||||||
|
"(",
|
||||||
|
assignee,
|
||||||
|
".", getDotKey(true),
|
||||||
|
createAssignment(),
|
||||||
|
_createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
|
||||||
|
")",
|
||||||
|
].join("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")";
|
return canThrow && rng(10) == 0 ? expr : "(" + assignee + " && " + expr + ")";
|
||||||
default:
|
default:
|
||||||
return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
|
return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
|
||||||
@@ -1351,7 +1591,14 @@ function patch_try_catch(orig, toplevel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var minify_options = require("./options.json").map(JSON.stringify);
|
var minify_options = require("./options.json");
|
||||||
|
if (typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
|
||||||
|
minify_options.forEach(function(o) {
|
||||||
|
if (!("output" in o)) o.output = {};
|
||||||
|
o.output.v8 = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
minify_options = minify_options.map(JSON.stringify);
|
||||||
var original_code, original_result, errored;
|
var original_code, original_result, errored;
|
||||||
var uglify_code, uglify_result, ok;
|
var uglify_code, uglify_result, ok;
|
||||||
for (var round = 1; round <= num_iterations; round++) {
|
for (var round = 1; round <= num_iterations; round++) {
|
||||||
|
|||||||
Reference in New Issue
Block a user