Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bba7cd0a70 | ||
|
|
e1b2026929 | ||
|
|
c319030373 | ||
|
|
47b63ed1a0 | ||
|
|
7aefe97083 | ||
|
|
89198e0ad4 | ||
|
|
caea6aac81 | ||
|
|
f5224ca1f5 | ||
|
|
b7c49b72b3 | ||
|
|
8ce3c7d70f | ||
|
|
87cf715213 | ||
|
|
2c9c72e06c | ||
|
|
882968c68c | ||
|
|
acc2d7d845 | ||
|
|
9a5aede941 | ||
|
|
e6dd471f8f | ||
|
|
0f55bd92f1 | ||
|
|
7d9dad0289 | ||
|
|
44e494f16f | ||
|
|
2415a72e75 | ||
|
|
9c0718b162 | ||
|
|
d2c50ace99 | ||
|
|
1b646d3bc4 | ||
|
|
82d2aa4acf | ||
|
|
c1256c399a | ||
|
|
2c637fea8a | ||
|
|
4fa54b075c | ||
|
|
ab82be82b2 | ||
|
|
02fdcfde01 | ||
|
|
a96f087ac3 | ||
|
|
75e9fd8417 | ||
|
|
f68e267830 |
32
README.md
32
README.md
@@ -1209,3 +1209,35 @@ To allow for better optimizations, the compiler makes various assumptions:
|
|||||||
`function({}, arguments) {}` will result in `SyntaxError` in earlier versions
|
`function({}, arguments) {}` will result in `SyntaxError` in earlier versions
|
||||||
of Chrome and Node.js - UglifyJS may modify the input which in turn may
|
of Chrome and Node.js - UglifyJS may modify the input which in turn may
|
||||||
suppress those errors.
|
suppress those errors.
|
||||||
|
- Earlier versions of Chrome and Node.js will throw `ReferenceError` with the
|
||||||
|
following:
|
||||||
|
```js
|
||||||
|
var a;
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch ({
|
||||||
|
[a]: b,
|
||||||
|
// ReferenceError: a is not defined
|
||||||
|
}) {
|
||||||
|
let a;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
- Later versions of JavaScript will throw `SyntaxError` with the following:
|
||||||
|
```js
|
||||||
|
a => {
|
||||||
|
let a;
|
||||||
|
};
|
||||||
|
// SyntaxError: Identifier 'a' has already been declared
|
||||||
|
```
|
||||||
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
- Later versions of JavaScript will throw `SyntaxError` with the following:
|
||||||
|
```js
|
||||||
|
try {
|
||||||
|
// ...
|
||||||
|
} catch ({ message: a }) {
|
||||||
|
var a;
|
||||||
|
}
|
||||||
|
// SyntaxError: Identifier 'a' has already been declared
|
||||||
|
```
|
||||||
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
|||||||
71
lib/ast.js
71
lib/ast.js
@@ -502,10 +502,9 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
}
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Lambda = DEFNODE("Lambda", "name argnames length_read uses_arguments", {
|
var AST_Lambda = DEFNODE("Lambda", "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",
|
|
||||||
argnames: "[(AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
|
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",
|
||||||
},
|
},
|
||||||
@@ -541,18 +540,53 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames length_read uses_arguments", {
|
|||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
var AST_Accessor = DEFNODE("Accessor", null, {
|
var AST_Accessor = DEFNODE("Accessor", null, {
|
||||||
$documentation: "A setter/getter function. The `name` property is always null.",
|
$documentation: "A getter/setter function",
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.name != null) throw new Error("name must be null");
|
if (this.name != null) throw new Error("name must be null");
|
||||||
},
|
},
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
|
|
||||||
function is_function(node) {
|
function is_function(node) {
|
||||||
return node instanceof AST_AsyncFunction || node instanceof AST_Function;
|
return node instanceof AST_Arrow || node instanceof AST_AsyncFunction || node instanceof AST_Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined", {
|
var AST_Arrow = DEFNODE("Arrow", "inlined value", {
|
||||||
|
$documentation: "An arrow function expression",
|
||||||
|
$propdoc: {
|
||||||
|
value: "[AST_Node?] simple return expression, or null if using function body.",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
node.argnames.forEach(function(argname) {
|
||||||
|
argname.walk(visitor);
|
||||||
|
});
|
||||||
|
if (node.value) {
|
||||||
|
node.value.walk(visitor);
|
||||||
|
} else {
|
||||||
|
walk_body(node, visitor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (this.name != null) throw new Error("name must be null");
|
||||||
|
if (this.uses_arguments) throw new Error("uses_arguments must be false");
|
||||||
|
if (this.value != null) {
|
||||||
|
must_be_expression(this, "value");
|
||||||
|
if (this.body.length) throw new Error("body must be empty if value exists");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}, AST_Lambda);
|
||||||
|
|
||||||
|
function is_async(node) {
|
||||||
|
return node instanceof AST_AsyncDefun || node instanceof AST_AsyncFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", {
|
||||||
$documentation: "An asynchronous function expression",
|
$documentation: "An asynchronous function expression",
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolLambda?] the name of this function",
|
||||||
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.name != null) {
|
if (this.name != null) {
|
||||||
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
|
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
|
||||||
@@ -560,8 +594,11 @@ var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined", {
|
|||||||
},
|
},
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
|
|
||||||
var AST_Function = DEFNODE("Function", "inlined", {
|
var AST_Function = DEFNODE("Function", "inlined name", {
|
||||||
$documentation: "A function expression",
|
$documentation: "A function expression",
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolLambda?] the name of this function",
|
||||||
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.name != null) {
|
if (this.name != null) {
|
||||||
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
|
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
|
||||||
@@ -573,15 +610,21 @@ function is_defun(node) {
|
|||||||
return node instanceof AST_AsyncDefun || node instanceof AST_Defun;
|
return node instanceof AST_AsyncDefun || node instanceof AST_Defun;
|
||||||
}
|
}
|
||||||
|
|
||||||
var AST_AsyncDefun = DEFNODE("AsyncDefun", "inlined", {
|
var AST_AsyncDefun = DEFNODE("AsyncDefun", "inlined name", {
|
||||||
$documentation: "An asynchronous function definition",
|
$documentation: "An asynchronous function definition",
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolDefun] the name of this function",
|
||||||
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
|
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
|
||||||
},
|
},
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
|
|
||||||
var AST_Defun = DEFNODE("Defun", "inlined", {
|
var AST_Defun = DEFNODE("Defun", "inlined name", {
|
||||||
$documentation: "A function definition",
|
$documentation: "A function definition",
|
||||||
|
$propdoc: {
|
||||||
|
name: "[AST_SymbolDefun] the name of this function",
|
||||||
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
|
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
|
||||||
},
|
},
|
||||||
@@ -747,7 +790,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
|||||||
var AST_Catch = DEFNODE("Catch", "argname", {
|
var AST_Catch = DEFNODE("Catch", "argname", {
|
||||||
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
argname: "[AST_SymbolCatch?] symbol for the exception, or null if not present",
|
argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
@@ -757,9 +800,9 @@ var AST_Catch = DEFNODE("Catch", "argname", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (this.argname != null) {
|
if (this.argname != null) validate_destructured(this.argname, function(node) {
|
||||||
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
if (!(node instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
||||||
}
|
});
|
||||||
},
|
},
|
||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
@@ -829,7 +872,7 @@ var AST_Var = DEFNODE("Var", 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] name of the variable",
|
name: "[AST_Destructured|AST_SymbolVar] 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"
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
@@ -1275,7 +1318,7 @@ var AST_Label = DEFNODE("Label", "references", {
|
|||||||
}
|
}
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolRef = DEFNODE("SymbolRef", "fixed", {
|
var AST_SymbolRef = DEFNODE("SymbolRef", "fixed in_arg", {
|
||||||
$documentation: "Reference to some symbol (not definition/declaration)",
|
$documentation: "Reference to some symbol (not definition/declaration)",
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
|
|||||||
385
lib/compress.js
385
lib/compress.js
@@ -49,6 +49,7 @@ function Compressor(options, false_by_default) {
|
|||||||
TreeTransformer.call(this, this.before, this.after);
|
TreeTransformer.call(this, this.before, this.after);
|
||||||
this.options = defaults(options, {
|
this.options = defaults(options, {
|
||||||
arguments : !false_by_default,
|
arguments : !false_by_default,
|
||||||
|
arrows : !false_by_default,
|
||||||
assignments : !false_by_default,
|
assignments : !false_by_default,
|
||||||
booleans : !false_by_default,
|
booleans : !false_by_default,
|
||||||
collapse_vars : !false_by_default,
|
collapse_vars : !false_by_default,
|
||||||
@@ -425,6 +426,9 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
if (scope.uses_arguments) scope.each_argname(function(node) {
|
||||||
|
node.definition().last_ref = false;
|
||||||
|
});
|
||||||
if (compressor.option("ie8")) scope.variables.each(function(def) {
|
if (compressor.option("ie8")) scope.variables.each(function(def) {
|
||||||
var d = def.orig[0].definition();
|
var d = def.orig[0].definition();
|
||||||
if (d !== def) d.fixed = false;
|
if (d !== def) d.fixed = false;
|
||||||
@@ -478,7 +482,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function push_ref(def, ref) {
|
function push_ref(def, ref) {
|
||||||
def.references.push(ref);
|
def.references.push(ref);
|
||||||
def.last_ref = ref;
|
if (def.last_ref !== false) def.last_ref = ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
function safe_to_read(tw, def) {
|
function safe_to_read(tw, def) {
|
||||||
@@ -591,7 +595,14 @@ merge(Compressor.prototype, {
|
|||||||
var expr = node.expression;
|
var expr = node.expression;
|
||||||
if (!(expr instanceof AST_SymbolRef)) return;
|
if (!(expr instanceof AST_SymbolRef)) return;
|
||||||
var def = expr.definition();
|
var def = expr.definition();
|
||||||
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
|
if (!is_arguments(def)) return;
|
||||||
|
var key = node.property;
|
||||||
|
if (key.is_constant()) key = key.value;
|
||||||
|
if (!(key instanceof AST_Node) && !/^[1-9]*[0-9]$/.test(key)) return;
|
||||||
|
def.reassigned = true;
|
||||||
|
(key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
|
||||||
|
if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function scan_declaration(tw, lhs, fixed, visit) {
|
function scan_declaration(tw, lhs, fixed, visit) {
|
||||||
@@ -644,7 +655,14 @@ merge(Compressor.prototype, {
|
|||||||
fixed = save;
|
fixed = save;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
visit(node, fixed);
|
visit(node, fixed, function() {
|
||||||
|
var save_len = tw.stack.length;
|
||||||
|
for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
|
||||||
|
tw.stack.push(scanner.stack[i]);
|
||||||
|
}
|
||||||
|
node.walk(tw);
|
||||||
|
tw.stack.length = save_len;
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
lhs.walk(scanner);
|
lhs.walk(scanner);
|
||||||
@@ -664,6 +682,53 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reduce_iife(tw, descend, compressor) {
|
||||||
|
var fn = this;
|
||||||
|
fn.inlined = false;
|
||||||
|
var iife = tw.parent();
|
||||||
|
var hit = fn instanceof AST_AsyncFunction;
|
||||||
|
var aborts = false;
|
||||||
|
fn.walk(new TreeWalker(function(node) {
|
||||||
|
if (hit) return aborts = true;
|
||||||
|
if (node instanceof AST_Return) return hit = true;
|
||||||
|
if (node instanceof AST_Scope && node !== fn) return true;
|
||||||
|
}));
|
||||||
|
if (aborts) push(tw);
|
||||||
|
reset_variables(tw, compressor, fn);
|
||||||
|
// Virtually turn IIFE parameters into variable definitions:
|
||||||
|
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
|
||||||
|
// So existing transformation rules can work on them.
|
||||||
|
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
||||||
|
fn.argnames.forEach(function(arg, i) {
|
||||||
|
var value = iife.args[i];
|
||||||
|
scan_declaration(tw, arg, function() {
|
||||||
|
var j = fn.argnames.indexOf(arg);
|
||||||
|
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
|
||||||
|
}, function(node, fixed) {
|
||||||
|
var d = node.definition();
|
||||||
|
if (safe && d.fixed === undefined) {
|
||||||
|
mark(tw, d);
|
||||||
|
tw.loop_ids[d.id] = tw.in_loop;
|
||||||
|
var value = iife.args[i];
|
||||||
|
d.fixed = fixed;
|
||||||
|
d.fixed.assigns = [ arg ];
|
||||||
|
} else {
|
||||||
|
d.fixed = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (fn instanceof AST_Arrow && fn.value) {
|
||||||
|
fn.value.walk(tw);
|
||||||
|
} else {
|
||||||
|
walk_body(fn, tw);
|
||||||
|
}
|
||||||
|
var safe_ids = tw.safe_ids;
|
||||||
|
pop(tw);
|
||||||
|
walk_defuns(tw, fn);
|
||||||
|
if (!aborts) tw.safe_ids = safe_ids;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
def(AST_Assign, function(tw, descend, compressor) {
|
def(AST_Assign, function(tw, descend, compressor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
var left = node.left;
|
var left = node.left;
|
||||||
@@ -678,10 +743,10 @@ merge(Compressor.prototype, {
|
|||||||
node.right.walk(tw);
|
node.right.walk(tw);
|
||||||
scan_declaration(tw, left, function() {
|
scan_declaration(tw, left, function() {
|
||||||
return node.right;
|
return node.right;
|
||||||
}, function(sym, fixed) {
|
}, function(sym, fixed, walk) {
|
||||||
if (!(sym instanceof AST_SymbolRef)) {
|
if (!(sym instanceof AST_SymbolRef)) {
|
||||||
mark_assignment_to_arguments(sym);
|
mark_assignment_to_arguments(sym);
|
||||||
sym.walk(tw);
|
walk();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var d = sym.definition();
|
var d = sym.definition();
|
||||||
@@ -696,7 +761,7 @@ merge(Compressor.prototype, {
|
|||||||
sym.fixed = d.fixed = fixed;
|
sym.fixed = d.fixed = fixed;
|
||||||
sym.fixed.assigns = [ node ];
|
sym.fixed.assigns = [ node ];
|
||||||
} else {
|
} else {
|
||||||
sym.walk(tw);
|
walk();
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -763,10 +828,14 @@ merge(Compressor.prototype, {
|
|||||||
tw.find_parent(AST_Scope).may_call_this();
|
tw.find_parent(AST_Scope).may_call_this();
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
if (is_function(exp)) {
|
if (is_function(exp)) {
|
||||||
|
var iife = !exp.name;
|
||||||
this.args.forEach(function(arg) {
|
this.args.forEach(function(arg) {
|
||||||
arg.walk(tw);
|
arg.walk(tw);
|
||||||
|
if (arg instanceof AST_Spread) iife = false;
|
||||||
});
|
});
|
||||||
|
if (iife) exp.reduce_vars = reduce_iife;
|
||||||
exp.walk(tw);
|
exp.walk(tw);
|
||||||
|
if (iife) delete exp.reduce_vars;
|
||||||
return true;
|
return true;
|
||||||
} else if (exp instanceof AST_SymbolRef) {
|
} else if (exp instanceof AST_SymbolRef) {
|
||||||
var def = exp.definition();
|
var def = exp.definition();
|
||||||
@@ -843,72 +912,22 @@ merge(Compressor.prototype, {
|
|||||||
init.walk(tw);
|
init.walk(tw);
|
||||||
if (init instanceof AST_Definitions) {
|
if (init instanceof AST_Definitions) {
|
||||||
init.definitions[0].name.match_symbol(function(node) {
|
init.definitions[0].name.match_symbol(function(node) {
|
||||||
if (node instanceof AST_SymbolDeclaration) node.definition().fixed = false;
|
if (node instanceof AST_SymbolDeclaration) {
|
||||||
|
var def = node.definition();
|
||||||
|
def.assignments++;
|
||||||
|
def.fixed = false;
|
||||||
|
}
|
||||||
}, true);
|
}, true);
|
||||||
} else if (init instanceof AST_SymbolRef) {
|
} else if (init instanceof AST_SymbolRef && !init.is_immutable()) {
|
||||||
init.definition().fixed = false;
|
var def = init.definition();
|
||||||
|
def.assignments++;
|
||||||
|
def.fixed = false;
|
||||||
}
|
}
|
||||||
this.body.walk(tw);
|
this.body.walk(tw);
|
||||||
pop(tw);
|
pop(tw);
|
||||||
tw.in_loop = saved_loop;
|
tw.in_loop = saved_loop;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
def(AST_Function, function(tw, descend, compressor) {
|
|
||||||
var fn = this;
|
|
||||||
fn.inlined = false;
|
|
||||||
var iife;
|
|
||||||
if (!fn.name
|
|
||||||
&& (iife = tw.parent()) instanceof AST_Call
|
|
||||||
&& iife.expression === fn
|
|
||||||
&& all(iife.args, function(arg) {
|
|
||||||
return !(arg instanceof AST_Spread);
|
|
||||||
})) {
|
|
||||||
var hit = false;
|
|
||||||
var aborts = false;
|
|
||||||
fn.walk(new TreeWalker(function(node) {
|
|
||||||
if (hit) return aborts = true;
|
|
||||||
if (node instanceof AST_Return) return hit = true;
|
|
||||||
if (node instanceof AST_Scope && node !== fn) return true;
|
|
||||||
}));
|
|
||||||
if (aborts) push(tw);
|
|
||||||
reset_variables(tw, compressor, fn);
|
|
||||||
// Virtually turn IIFE parameters into variable definitions:
|
|
||||||
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
|
|
||||||
// So existing transformation rules can work on them.
|
|
||||||
var safe = !fn.uses_arguments || tw.has_directive("use strict");
|
|
||||||
fn.argnames.forEach(function(arg, i) {
|
|
||||||
var value = iife.args[i];
|
|
||||||
scan_declaration(tw, arg, function() {
|
|
||||||
var j = fn.argnames.indexOf(arg);
|
|
||||||
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
|
|
||||||
}, function(node, fixed) {
|
|
||||||
var d = node.definition();
|
|
||||||
if (safe && d.fixed === undefined) {
|
|
||||||
mark(tw, d);
|
|
||||||
tw.loop_ids[d.id] = tw.in_loop;
|
|
||||||
var value = iife.args[i];
|
|
||||||
d.fixed = fixed;
|
|
||||||
d.fixed.assigns = [ arg ];
|
|
||||||
} else {
|
|
||||||
d.fixed = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
walk_body(fn, tw);
|
|
||||||
var safe_ids = tw.safe_ids;
|
|
||||||
pop(tw);
|
|
||||||
walk_defuns(tw, fn);
|
|
||||||
if (!aborts) tw.safe_ids = safe_ids;
|
|
||||||
} else {
|
|
||||||
push(tw);
|
|
||||||
reset_variables(tw, compressor, fn);
|
|
||||||
descend();
|
|
||||||
pop(tw);
|
|
||||||
if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
|
|
||||||
walk_defuns(tw, fn);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
def(AST_If, function(tw) {
|
def(AST_If, function(tw) {
|
||||||
this.condition.walk(tw);
|
this.condition.walk(tw);
|
||||||
push(tw);
|
push(tw);
|
||||||
@@ -928,12 +947,14 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
def(AST_Lambda, function(tw, descend, compressor) {
|
def(AST_Lambda, function(tw, descend, compressor) {
|
||||||
this.inlined = false;
|
var fn = this;
|
||||||
|
fn.inlined = false;
|
||||||
push(tw);
|
push(tw);
|
||||||
reset_variables(tw, compressor, this);
|
reset_variables(tw, compressor, fn);
|
||||||
descend();
|
descend();
|
||||||
pop(tw);
|
pop(tw);
|
||||||
walk_defuns(tw, this);
|
if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
|
||||||
|
walk_defuns(tw, fn);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
def(AST_Switch, function(tw, descend, compressor) {
|
def(AST_Switch, function(tw, descend, compressor) {
|
||||||
@@ -1040,7 +1061,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
def(AST_Unary, function(tw, descend) {
|
def(AST_Unary, function(tw, descend) {
|
||||||
var node = this;
|
var node = this;
|
||||||
if (!unary_arithmetic[node.operator]) return;
|
if (!UNARY_POSTFIX[node.operator]) return;
|
||||||
var exp = node.expression;
|
var exp = node.expression;
|
||||||
if (!(exp instanceof AST_SymbolRef)) {
|
if (!(exp instanceof AST_SymbolRef)) {
|
||||||
mark_assignment_to_arguments(exp);
|
mark_assignment_to_arguments(exp);
|
||||||
@@ -1365,7 +1386,8 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function is_iife_call(node) {
|
function is_iife_call(node) {
|
||||||
if (node.TYPE != "Call") return false;
|
if (node.TYPE != "Call") return false;
|
||||||
return is_function(node.expression) || is_iife_call(node.expression);
|
var exp = node.expression;
|
||||||
|
return exp instanceof AST_AsyncFunction || exp instanceof AST_Function || is_iife_call(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_undeclared_ref(node) {
|
function is_undeclared_ref(node) {
|
||||||
@@ -1727,7 +1749,11 @@ merge(Compressor.prototype, {
|
|||||||
if (node instanceof AST_LoopControl) return true;
|
if (node instanceof AST_LoopControl) return true;
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
if (node.is_declared(compressor)) {
|
if (node.is_declared(compressor)) {
|
||||||
if (node.fixed_value() || can_drop_symbol(node)) return false;
|
if (node.fixed_value()) return false;
|
||||||
|
if (can_drop_symbol(node)) {
|
||||||
|
return !(parent instanceof AST_PropAccess && parent.expression === node)
|
||||||
|
&& is_arguments(node.definition());
|
||||||
|
}
|
||||||
} else if (parent instanceof AST_Assign && parent.operator == "=" && parent.left === node) {
|
} else if (parent instanceof AST_Assign && parent.operator == "=" && parent.left === node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1767,7 +1793,9 @@ merge(Compressor.prototype, {
|
|||||||
can_replace = false;
|
can_replace = false;
|
||||||
var after = stop_after;
|
var after = stop_after;
|
||||||
var if_hit = stop_if_hit;
|
var if_hit = stop_if_hit;
|
||||||
for (var i = 0; !abort && i < fn.body.length; i++) {
|
if (fn instanceof AST_Arrow && fn.value) {
|
||||||
|
fn.value.transform(scanner);
|
||||||
|
} else for (var i = 0; !abort && i < fn.body.length; i++) {
|
||||||
var stat = fn.body[i];
|
var stat = fn.body[i];
|
||||||
if (stat instanceof AST_Return) {
|
if (stat instanceof AST_Return) {
|
||||||
if (stat.value) stat.value.transform(scanner);
|
if (stat.value) stat.value.transform(scanner);
|
||||||
@@ -1794,7 +1822,9 @@ merge(Compressor.prototype, {
|
|||||||
return compressor.option("ie8") && node.name && lvalues.has(node.name.name);
|
return compressor.option("ie8") && node.name && lvalues.has(node.name.name);
|
||||||
}
|
}
|
||||||
if (node instanceof AST_PropAccess) {
|
if (node instanceof AST_PropAccess) {
|
||||||
return side_effects || !value_def && node.expression.may_throw_on_access(compressor);
|
var exp = node.expression;
|
||||||
|
return side_effects || !value_def && exp.may_throw_on_access(compressor)
|
||||||
|
|| exp instanceof AST_SymbolRef && is_arguments(exp.definition());
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Spread) return true;
|
if (node instanceof AST_Spread) return true;
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
@@ -1967,7 +1997,7 @@ merge(Compressor.prototype, {
|
|||||||
extract_candidates(expr.expression);
|
extract_candidates(expr.expression);
|
||||||
expr.body.forEach(extract_candidates);
|
expr.body.forEach(extract_candidates);
|
||||||
} else if (expr instanceof AST_Unary) {
|
} else if (expr instanceof AST_Unary) {
|
||||||
if (unary_arithmetic[expr.operator]) {
|
if (UNARY_POSTFIX[expr.operator]) {
|
||||||
candidates.push(hit_stack.slice());
|
candidates.push(hit_stack.slice());
|
||||||
} else {
|
} else {
|
||||||
extract_candidates(expr.expression);
|
extract_candidates(expr.expression);
|
||||||
@@ -3462,8 +3492,6 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
var lazy_op = makePredicate("&& ||");
|
var lazy_op = makePredicate("&& ||");
|
||||||
var unary_arithmetic = makePredicate("++ --");
|
|
||||||
var unary_side_effects = makePredicate("delete ++ --");
|
|
||||||
|
|
||||||
function is_lhs(node, parent) {
|
function is_lhs(node, parent) {
|
||||||
if (parent instanceof AST_Assign) return parent.left === node && node;
|
if (parent instanceof AST_Assign) return parent.left === node && node;
|
||||||
@@ -3566,12 +3594,20 @@ merge(Compressor.prototype, {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("first_statement", function() {
|
function skip_directives(body) {
|
||||||
var body = this.body;
|
|
||||||
for (var i = 0; i < body.length; i++) {
|
for (var i = 0; i < body.length; i++) {
|
||||||
var stat = body[i];
|
var stat = body[i];
|
||||||
if (!(stat instanceof AST_Directive)) return stat;
|
if (!(stat instanceof AST_Directive)) return stat;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
AST_Arrow.DEFMETHOD("first_statement", function() {
|
||||||
|
if (this.value) return make_node(AST_Return, this.value, {
|
||||||
|
value: this.value
|
||||||
|
});
|
||||||
|
return skip_directives(this.body);
|
||||||
|
});
|
||||||
|
AST_Lambda.DEFMETHOD("first_statement", function() {
|
||||||
|
return skip_directives(this.body);
|
||||||
});
|
});
|
||||||
|
|
||||||
function try_evaluate(compressor, node) {
|
function try_evaluate(compressor, node) {
|
||||||
@@ -3706,7 +3742,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
var scan_modified = new TreeWalker(function(node) {
|
var scan_modified = new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Assign) modified(node.left);
|
if (node instanceof AST_Assign) modified(node.left);
|
||||||
if (node instanceof AST_Unary && unary_arithmetic[node.operator]) modified(node.expression);
|
if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
|
||||||
});
|
});
|
||||||
function modified(node) {
|
function modified(node) {
|
||||||
if (node instanceof AST_PropAccess) {
|
if (node instanceof AST_PropAccess) {
|
||||||
@@ -4163,6 +4199,9 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Statement, function() {
|
def(AST_Statement, function() {
|
||||||
throw new Error("Cannot negate a statement");
|
throw new Error("Cannot negate a statement");
|
||||||
});
|
});
|
||||||
|
def(AST_Arrow, function() {
|
||||||
|
return basic_negation(this);
|
||||||
|
});
|
||||||
def(AST_AsyncFunction, function() {
|
def(AST_AsyncFunction, function() {
|
||||||
return basic_negation(this);
|
return basic_negation(this);
|
||||||
});
|
});
|
||||||
@@ -4561,6 +4600,10 @@ merge(Compressor.prototype, {
|
|||||||
result = false;
|
result = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_This) {
|
||||||
|
if (scopes.length == 0 && self instanceof AST_Arrow) result = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@@ -4644,6 +4687,25 @@ merge(Compressor.prototype, {
|
|||||||
return trim_block(self);
|
return trim_block(self);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OPT(AST_Arrow, function(self, compressor) {
|
||||||
|
if (!compressor.option("arrows")) return self;
|
||||||
|
var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
|
||||||
|
switch (body.length) {
|
||||||
|
case 1:
|
||||||
|
var stat = body[0];
|
||||||
|
if (stat instanceof AST_Return) {
|
||||||
|
self.body.length = 0;
|
||||||
|
self.value = stat.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
self.body = body;
|
||||||
|
self.value = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_Function, function(self, compressor) {
|
OPT(AST_Function, function(self, compressor) {
|
||||||
self.body = tighten_body(self.body, compressor);
|
self.body = tighten_body(self.body, compressor);
|
||||||
if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
|
if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
|
||||||
@@ -4758,7 +4820,7 @@ merge(Compressor.prototype, {
|
|||||||
if (node instanceof AST_Call) {
|
if (node instanceof AST_Call) {
|
||||||
var exp = node.expression;
|
var exp = node.expression;
|
||||||
var tail = exp.tail_node();
|
var tail = exp.tail_node();
|
||||||
if (!(tail instanceof AST_Function)) return;
|
if (!is_function(tail)) return;
|
||||||
if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
|
if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
|
||||||
node.walk(tw);
|
node.walk(tw);
|
||||||
});
|
});
|
||||||
@@ -4869,7 +4931,11 @@ merge(Compressor.prototype, {
|
|||||||
argname.mark_symbol(marker, scanner);
|
argname.mark_symbol(marker, scanner);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Arrow && node.value) {
|
||||||
|
node.value.walk(tw);
|
||||||
|
} else {
|
||||||
walk_body(node, tw);
|
walk_body(node, tw);
|
||||||
|
}
|
||||||
pop();
|
pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -4902,11 +4968,13 @@ merge(Compressor.prototype, {
|
|||||||
walk_body(node, tw);
|
walk_body(node, tw);
|
||||||
pop();
|
pop();
|
||||||
if (node.bcatch) {
|
if (node.bcatch) {
|
||||||
if (node.bcatch.argname) {
|
if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
|
||||||
var def = node.bcatch.argname.definition();
|
if (node instanceof AST_SymbolCatch) {
|
||||||
|
var def = node.definition();
|
||||||
references[def.id] = false;
|
references[def.id] = false;
|
||||||
if (def = def.redefined()) references[def.id] = false;
|
if (def = def.redefined()) references[def.id] = false;
|
||||||
}
|
}
|
||||||
|
}, tw);
|
||||||
push();
|
push();
|
||||||
if (node.bfinally) segment.block = node.bcatch;
|
if (node.bfinally) segment.block = node.bcatch;
|
||||||
walk_body(node.bcatch, tw);
|
walk_body(node.bcatch, tw);
|
||||||
@@ -4916,7 +4984,7 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Unary) {
|
if (node instanceof AST_Unary) {
|
||||||
if (!unary_arithmetic[node.operator]) return;
|
if (!UNARY_POSTFIX[node.operator]) return;
|
||||||
var sym = node.expression;
|
var sym = node.expression;
|
||||||
if (!(sym instanceof AST_SymbolRef)) return;
|
if (!(sym instanceof AST_SymbolRef)) return;
|
||||||
mark(sym, true, true);
|
mark(sym, true, true);
|
||||||
@@ -5270,6 +5338,34 @@ merge(Compressor.prototype, {
|
|||||||
var unused_fn_names = [];
|
var unused_fn_names = [];
|
||||||
var calls_to_drop_args = [];
|
var calls_to_drop_args = [];
|
||||||
var fns_with_marked_args = [];
|
var fns_with_marked_args = [];
|
||||||
|
var trimmer = new TreeTransformer(function(node) {
|
||||||
|
if (node instanceof AST_DestructuredArray) {
|
||||||
|
var trim = true;
|
||||||
|
for (var i = node.elements.length; --i >= 0;) {
|
||||||
|
var sym = node.elements[i];
|
||||||
|
if (!(sym instanceof AST_SymbolDeclaration)) {
|
||||||
|
node.elements[i] = sym.transform(trimmer);
|
||||||
|
trim = false;
|
||||||
|
} else if (sym.definition().id in in_use_ids) {
|
||||||
|
trim = false;
|
||||||
|
} else if (trim) {
|
||||||
|
node.elements.pop();
|
||||||
|
} else {
|
||||||
|
node.elements[i] = make_node(AST_Hole, sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_DestructuredKeyVal) {
|
||||||
|
if (!(node.value instanceof AST_SymbolDeclaration)) {
|
||||||
|
node.value = node.value.transform(trimmer);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (typeof node.key != "string") return node;
|
||||||
|
if (node.value.definition().id in in_use_ids) return node;
|
||||||
|
return List.skip;
|
||||||
|
}
|
||||||
|
});
|
||||||
var tt = new TreeTransformer(function(node, descend, in_list) {
|
var tt = new TreeTransformer(function(node, descend, in_list) {
|
||||||
var parent = tt.parent();
|
var parent = tt.parent();
|
||||||
if (drop_vars) {
|
if (drop_vars) {
|
||||||
@@ -5336,34 +5432,7 @@ merge(Compressor.prototype, {
|
|||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
var sym = a[i];
|
var sym = a[i];
|
||||||
if (sym instanceof AST_Destructured) {
|
if (sym instanceof AST_Destructured) {
|
||||||
sym.transform(new TreeTransformer(function(node) {
|
sym.transform(trimmer);
|
||||||
if (node instanceof AST_DestructuredArray) {
|
|
||||||
var trim = true;
|
|
||||||
for (var i = node.elements.length; --i >= 0;) {
|
|
||||||
var sym = node.elements[i];
|
|
||||||
if (!(sym instanceof AST_SymbolFunarg)) {
|
|
||||||
node.elements[i] = sym.transform(this);
|
|
||||||
trim = false;
|
|
||||||
} else if (sym.definition().id in in_use_ids) {
|
|
||||||
trim = false;
|
|
||||||
} else if (trim) {
|
|
||||||
node.elements.pop();
|
|
||||||
} else {
|
|
||||||
node.elements[i] = make_node(AST_Hole, sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_DestructuredKeyVal) {
|
|
||||||
if (!(node.value instanceof AST_SymbolFunarg)) {
|
|
||||||
node.value = node.value.transform(this);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
if (typeof node.key != "string") return node;
|
|
||||||
if (node.value.definition().id in in_use_ids) return node;
|
|
||||||
return List.skip;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
trim = false;
|
trim = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -5382,6 +5451,9 @@ merge(Compressor.prototype, {
|
|||||||
fns_with_marked_args.push(node);
|
fns_with_marked_args.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
|
||||||
|
node.argname.transform(trimmer);
|
||||||
|
}
|
||||||
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
|
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
|
||||||
// place uninitialized names at the start
|
// place uninitialized names at the start
|
||||||
var body = [], head = [], tail = [];
|
var body = [], head = [], tail = [];
|
||||||
@@ -5813,7 +5885,15 @@ merge(Compressor.prototype, {
|
|||||||
if (def.scope === self) assignments.add(def.id, node);
|
if (def.scope === self) assignments.add(def.id, node);
|
||||||
}
|
}
|
||||||
var node_def, props = [], sym = assign_as_unused(node, props);
|
var node_def, props = [], sym = assign_as_unused(node, props);
|
||||||
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
|
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())
|
||||||
|
&& !(is_arguments(node_def) && !all(self.argnames, function(argname) {
|
||||||
|
return !argname.match_symbol(function(node) {
|
||||||
|
if (node instanceof AST_SymbolFunarg) {
|
||||||
|
var def = node.definition();
|
||||||
|
return def.references.length > def.replaced;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}))) {
|
||||||
props.forEach(function(prop) {
|
props.forEach(function(prop) {
|
||||||
prop.walk(tw);
|
prop.walk(tw);
|
||||||
});
|
});
|
||||||
@@ -6309,6 +6389,7 @@ merge(Compressor.prototype, {
|
|||||||
})) return this;
|
})) return this;
|
||||||
return make_sequence(this, values.map(convert_spread));
|
return make_sequence(this, values.map(convert_spread));
|
||||||
});
|
});
|
||||||
|
def(AST_Arrow, return_null);
|
||||||
def(AST_Assign, function(compressor) {
|
def(AST_Assign, function(compressor) {
|
||||||
var left = this.left;
|
var left = this.left;
|
||||||
if (left instanceof AST_PropAccess) {
|
if (left instanceof AST_PropAccess) {
|
||||||
@@ -7207,7 +7288,9 @@ merge(Compressor.prototype, {
|
|||||||
self.body = tighten_body(self.body, compressor);
|
self.body = tighten_body(self.body, compressor);
|
||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
if (has_declarations_only(self)
|
if (has_declarations_only(self)
|
||||||
&& !(self.bcatch && self.bcatch.argname && !can_drop_symbol(self.bcatch.argname))) {
|
&& !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
|
||||||
|
return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
|
||||||
|
}, true))) {
|
||||||
var body = [];
|
var body = [];
|
||||||
if (self.bcatch) {
|
if (self.bcatch) {
|
||||||
extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
|
extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
|
||||||
@@ -7695,7 +7778,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
||||||
var is_func = fn instanceof AST_Defun || fn instanceof AST_Function;
|
var is_func = fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function;
|
||||||
var stat = is_func && fn.first_statement();
|
var stat = is_func && fn.first_statement();
|
||||||
var can_inline = is_func
|
var can_inline = is_func
|
||||||
&& compressor.option("inline")
|
&& compressor.option("inline")
|
||||||
@@ -7708,7 +7791,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
if (can_inline && stat instanceof AST_Return) {
|
if (can_inline && stat instanceof AST_Return) {
|
||||||
var value = stat.value;
|
var value = stat.value;
|
||||||
if (exp === fn && (!value || value.is_constant_expression())) {
|
if (exp === fn && (!value || value.is_constant_expression() && safe_from_await(value))) {
|
||||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||||
return make_sequence(self, args).optimize(compressor);
|
return make_sequence(self, args).optimize(compressor);
|
||||||
}
|
}
|
||||||
@@ -7765,10 +7848,11 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (compressor.option("side_effects")
|
if (compressor.option("side_effects")
|
||||||
&& all(fn.body, is_empty)
|
&& all(fn.body, is_empty)
|
||||||
|
&& (fn !== exp || fn_name_unused(fn, compressor))
|
||||||
|
&& !(fn instanceof AST_Arrow && fn.value)
|
||||||
&& all(fn.argnames, function(argname) {
|
&& all(fn.argnames, function(argname) {
|
||||||
return !(argname instanceof AST_Destructured);
|
return !(argname instanceof AST_Destructured);
|
||||||
})
|
})) {
|
||||||
&& (fn !== exp || fn_name_unused(fn, compressor))) {
|
|
||||||
var args = self.args.map(function(arg) {
|
var args = self.args.map(function(arg) {
|
||||||
return arg instanceof AST_Spread ? make_node(AST_Array, arg, {
|
return arg instanceof AST_Spread ? make_node(AST_Array, arg, {
|
||||||
elements: [ arg ],
|
elements: [ arg ],
|
||||||
@@ -7796,6 +7880,26 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return try_evaluate(compressor, self);
|
return try_evaluate(compressor, self);
|
||||||
|
|
||||||
|
function safe_from_await(node) {
|
||||||
|
if (!is_async(scope || compressor.find_parent(AST_Scope))) return true;
|
||||||
|
var safe = true;
|
||||||
|
var tw = new TreeWalker(function(node) {
|
||||||
|
if (!safe) return true;
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
if (node === fn) return;
|
||||||
|
if (node instanceof AST_Arrow) {
|
||||||
|
for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
|
||||||
|
} else if (is_defun(node) && node.name.name == "await") {
|
||||||
|
safe = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Symbol && node.name == "await" && node !== fn.name) safe = false;
|
||||||
|
});
|
||||||
|
node.walk(tw);
|
||||||
|
return safe;
|
||||||
|
}
|
||||||
|
|
||||||
function return_value(stat) {
|
function return_value(stat) {
|
||||||
if (!stat) return make_node(AST_Undefined, self);
|
if (!stat) return make_node(AST_Undefined, self);
|
||||||
if (stat instanceof AST_Return) return stat.value || make_node(AST_Undefined, self);
|
if (stat instanceof AST_Return) return stat.value || make_node(AST_Undefined, self);
|
||||||
@@ -7807,9 +7911,11 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
function can_flatten_body(stat) {
|
function can_flatten_body(stat) {
|
||||||
var len = fn.body.length;
|
var len = fn.body.length;
|
||||||
if (compressor.option("inline") < 3) {
|
if (len < 2) {
|
||||||
return len == 1 && return_value(stat);
|
stat = return_value(stat);
|
||||||
|
if (stat) return stat;
|
||||||
}
|
}
|
||||||
|
if (compressor.option("inline") < 3) return false;
|
||||||
stat = null;
|
stat = null;
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
var line = fn.body[i];
|
var line = fn.body[i];
|
||||||
@@ -7860,8 +7966,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Scope) return abort = true;
|
if (node instanceof AST_Scope) return abort = true;
|
||||||
if (verify_await && node instanceof AST_Symbol && node.name == "await") {
|
if (verify_await && node instanceof AST_Symbol && node.name == "await") {
|
||||||
var scope = compressor.find_parent(AST_Scope);
|
if (is_async(compressor.find_parent(AST_Scope))) return abort = true;
|
||||||
if (scope instanceof AST_AsyncDefun || scope instanceof AST_AsyncFunction) return abort = true;
|
|
||||||
verify_await = false;
|
verify_await = false;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
@@ -7964,20 +8069,15 @@ merge(Compressor.prototype, {
|
|||||||
} while (!(scope instanceof AST_Scope));
|
} while (!(scope instanceof AST_Scope));
|
||||||
insert = scope.body.indexOf(child) + 1;
|
insert = scope.body.indexOf(child) + 1;
|
||||||
if (!insert) return false;
|
if (!insert) return false;
|
||||||
if (scope instanceof AST_AsyncDefun || scope instanceof AST_AsyncFunction) {
|
if (!safe_from_await(fn)) return false;
|
||||||
var found = false;
|
var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
|
||||||
fn.walk(new TreeWalker(function(node) {
|
if (scope instanceof AST_Toplevel) {
|
||||||
if (found) return true;
|
if (compressor.toplevel.vars) {
|
||||||
if (node instanceof AST_Scope && node !== fn) {
|
defined["arguments"] = true;
|
||||||
if (is_defun(node) && node.name.name == "await") found = true;
|
} else {
|
||||||
return true;
|
safe_to_inject = false;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Symbol && node.name == "await" && node !== fn.name) return found = true;
|
|
||||||
}));
|
|
||||||
if (found) return false;
|
|
||||||
}
|
}
|
||||||
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
|
|
||||||
&& (exp !== fn || fn.parent_scope.resolve() === scope);
|
|
||||||
var inline = compressor.option("inline");
|
var inline = compressor.option("inline");
|
||||||
var used = Object.create(defined);
|
var used = Object.create(defined);
|
||||||
if (!can_inject_args(defined, used, inline >= 2 && safe_to_inject)) return false;
|
if (!can_inject_args(defined, used, inline >= 2 && safe_to_inject)) return false;
|
||||||
@@ -9825,9 +9925,10 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("arguments")
|
if (compressor.option("arguments")
|
||||||
&& expr instanceof AST_SymbolRef
|
&& expr instanceof AST_SymbolRef
|
||||||
&& is_arguments(def = expr.definition())
|
&& is_arguments(def = expr.definition())
|
||||||
|
&& !expr.in_arg
|
||||||
&& prop instanceof AST_Number
|
&& prop instanceof AST_Number
|
||||||
&& (fn = expr.scope.resolve()) === find_lambda()
|
&& (fn = def.scope) === find_lambda()
|
||||||
&& !(assigned && fn.uses_arguments === "d")) {
|
&& fn.uses_arguments < (assigned ? 2 : 3)) {
|
||||||
var index = prop.value;
|
var index = prop.value;
|
||||||
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
|
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
|
||||||
if (!def.deleted) def.deleted = [];
|
if (!def.deleted) def.deleted = [];
|
||||||
@@ -9836,8 +9937,14 @@ merge(Compressor.prototype, {
|
|||||||
var argname = fn.argnames[index];
|
var argname = fn.argnames[index];
|
||||||
if (def.deleted && def.deleted[index]) {
|
if (def.deleted && def.deleted[index]) {
|
||||||
argname = null;
|
argname = null;
|
||||||
|
} else if (argname instanceof AST_Destructured) {
|
||||||
|
argname = null;
|
||||||
} else if (argname && (compressor.has_directive("use strict")
|
} else if (argname && (compressor.has_directive("use strict")
|
||||||
|| !(fn_parent instanceof AST_Call && index < fn_parent.args.length))) {
|
|| fn.name
|
||||||
|
|| !(fn_parent instanceof AST_Call && index < fn_parent.args.length)
|
||||||
|
|| !all(fn.argnames, function(argname) {
|
||||||
|
return !(argname instanceof AST_Destructured);
|
||||||
|
}))) {
|
||||||
var arg_def = argname.definition();
|
var arg_def = argname.definition();
|
||||||
if (!compressor.option("reduce_vars")
|
if (!compressor.option("reduce_vars")
|
||||||
|| def.reassigned
|
|| def.reassigned
|
||||||
@@ -9927,6 +10034,7 @@ merge(Compressor.prototype, {
|
|||||||
while (p = compressor.parent(i++)) {
|
while (p = compressor.parent(i++)) {
|
||||||
if (p instanceof AST_Lambda) {
|
if (p instanceof AST_Lambda) {
|
||||||
if (p instanceof AST_Accessor) return;
|
if (p instanceof AST_Accessor) return;
|
||||||
|
if (p instanceof AST_Arrow) continue;
|
||||||
fn_parent = compressor.parent(i);
|
fn_parent = compressor.parent(i);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@@ -9934,13 +10042,14 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Arrow.DEFMETHOD("contains_this", return_false);
|
||||||
AST_Scope.DEFMETHOD("contains_this", function() {
|
AST_Scope.DEFMETHOD("contains_this", function() {
|
||||||
var result;
|
var result;
|
||||||
var self = this;
|
var self = this;
|
||||||
self.walk(new TreeWalker(function(node) {
|
self.walk(new TreeWalker(function(node) {
|
||||||
if (result) return true;
|
if (result) return true;
|
||||||
if (node instanceof AST_This) return result = true;
|
if (node instanceof AST_This) return result = true;
|
||||||
if (node !== self && node instanceof AST_Scope) return true;
|
if (node !== self && node instanceof AST_Scope && !(node instanceof AST_Arrow)) return true;
|
||||||
}));
|
}));
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
@@ -10116,7 +10225,7 @@ merge(Compressor.prototype, {
|
|||||||
flush();
|
flush();
|
||||||
values.push(prop);
|
values.push(prop);
|
||||||
}
|
}
|
||||||
if (found && !generated && typeof key == "string" && /^[0-9]+$/.test(key)) {
|
if (found && !generated && typeof key == "string" && /^[1-9]*[0-9]$/.test(key)) {
|
||||||
generated = true;
|
generated = true;
|
||||||
if (keys.has(key)) prop = keys.get(key)[0];
|
if (keys.has(key)) prop = keys.get(key)[0];
|
||||||
prop.key = make_node(AST_Number, prop, {
|
prop.key = make_node(AST_Number, prop, {
|
||||||
|
|||||||
@@ -678,7 +678,7 @@ 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.
|
||||||
function needs_parens_obj(output) {
|
function needs_parens_obj(output) {
|
||||||
return !output.has_parens() && first_in_statement(output);
|
return !output.has_parens() && first_in_statement(output, true);
|
||||||
}
|
}
|
||||||
PARENS(AST_Object, needs_parens_obj);
|
PARENS(AST_Object, needs_parens_obj);
|
||||||
|
|
||||||
@@ -691,6 +691,8 @@ function OutputStream(options) {
|
|||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||||
return p instanceof AST_Array
|
return p instanceof AST_Array
|
||||||
|
// () => (foo, bar)
|
||||||
|
|| p instanceof AST_Arrow && p.value === this
|
||||||
// await (foo, bar)
|
// await (foo, bar)
|
||||||
|| p instanceof AST_Await
|
|| p instanceof AST_Await
|
||||||
// 1 + (2, 3) + 4 ==> 8
|
// 1 + (2, 3) + 4 ==> 8
|
||||||
@@ -798,6 +800,9 @@ function OutputStream(options) {
|
|||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
if (p instanceof AST_Unary) return true;
|
if (p instanceof AST_Unary) return true;
|
||||||
}
|
}
|
||||||
|
PARENS(AST_Arrow, function(output) {
|
||||||
|
return needs_parens_assign_cond(this, output);
|
||||||
|
});
|
||||||
PARENS(AST_Assign, function(output) {
|
PARENS(AST_Assign, function(output) {
|
||||||
if (needs_parens_assign_cond(this, output)) return true;
|
if (needs_parens_assign_cond(this, output)) return true;
|
||||||
// v8 parser bug => workaround
|
// v8 parser bug => workaround
|
||||||
@@ -985,6 +990,25 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
/* -----[ functions ]----- */
|
||||||
|
DEFPRINT(AST_Arrow, function(output) {
|
||||||
|
var self = this;
|
||||||
|
if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg) {
|
||||||
|
self.argnames[0].print(output);
|
||||||
|
} else output.with_parens(function() {
|
||||||
|
self.argnames.forEach(function(arg, i) {
|
||||||
|
if (i) output.comma();
|
||||||
|
arg.print(output);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
output.space();
|
||||||
|
output.print("=>");
|
||||||
|
output.space();
|
||||||
|
if (self.value) {
|
||||||
|
self.value.print(output);
|
||||||
|
} else {
|
||||||
|
print_braced(self, output, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
function print_lambda(self, output) {
|
function print_lambda(self, output) {
|
||||||
if (self.name) {
|
if (self.name) {
|
||||||
output.space();
|
output.space();
|
||||||
@@ -1174,11 +1198,9 @@ function OutputStream(options) {
|
|||||||
// need to take some precautions here:
|
// need to take some precautions here:
|
||||||
// https://github.com/mishoo/UglifyJS/issues/60
|
// https://github.com/mishoo/UglifyJS/issues/60
|
||||||
if (noin) node.walk(new TreeWalker(function(node) {
|
if (noin) node.walk(new TreeWalker(function(node) {
|
||||||
if (parens || node instanceof AST_Scope) return true;
|
if (parens) return true;
|
||||||
if (node instanceof AST_Binary && node.operator == "in") {
|
if (node instanceof AST_Binary && node.operator == "in") return parens = true;
|
||||||
parens = true;
|
if (node instanceof AST_Scope && !(node instanceof AST_Arrow && node.value)) return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
node.print(output, parens);
|
node.print(output, parens);
|
||||||
}
|
}
|
||||||
|
|||||||
227
lib/parse.js
227
lib/parse.js
@@ -569,6 +569,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
}
|
}
|
||||||
if (is_digit(code)) return read_num();
|
if (is_digit(code)) return read_num();
|
||||||
if (PUNC_CHARS[ch]) return token("punc", next());
|
if (PUNC_CHARS[ch]) return token("punc", next());
|
||||||
|
if (looking_at("=>")) return token("punc", next() + next());
|
||||||
if (OPERATOR_CHARS[ch]) return read_operator();
|
if (OPERATOR_CHARS[ch]) return read_operator();
|
||||||
if (code == 92 || !NON_IDENTIFIER_CHARS[ch]) return read_word();
|
if (code == 92 || !NON_IDENTIFIER_CHARS[ch]) return read_word();
|
||||||
break;
|
break;
|
||||||
@@ -634,7 +635,7 @@ var PRECEDENCE = function(a, ret) {
|
|||||||
["*", "/", "%"]
|
["*", "/", "%"]
|
||||||
], {});
|
], {});
|
||||||
|
|
||||||
var ATOMIC_START_TOKEN = makePredicate("atom num string regexp name");
|
var ATOMIC_START_TOKEN = makePredicate("atom num regexp string");
|
||||||
|
|
||||||
/* -----[ Parser ]----- */
|
/* -----[ Parser ]----- */
|
||||||
|
|
||||||
@@ -741,7 +742,7 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function parenthesised() {
|
function parenthesised() {
|
||||||
expect("(");
|
expect("(");
|
||||||
var exp = expression(true);
|
var exp = expression();
|
||||||
expect(")");
|
expect(")");
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
@@ -769,7 +770,7 @@ function parse($TEXT, options) {
|
|||||||
switch (S.token.type) {
|
switch (S.token.type) {
|
||||||
case "string":
|
case "string":
|
||||||
var dir = S.in_directives;
|
var dir = S.in_directives;
|
||||||
var body = expression(true);
|
var body = expression();
|
||||||
if (dir) {
|
if (dir) {
|
||||||
if (body instanceof AST_String) {
|
if (body instanceof AST_String) {
|
||||||
var value = body.start.raw.slice(1, -1);
|
var value = body.start.raw.slice(1, -1);
|
||||||
@@ -887,7 +888,7 @@ function parse($TEXT, options) {
|
|||||||
if (is("punc", ";")) {
|
if (is("punc", ";")) {
|
||||||
next();
|
next();
|
||||||
} else if (!can_insert_semicolon()) {
|
} else if (!can_insert_semicolon()) {
|
||||||
value = expression(true);
|
value = expression();
|
||||||
semicolon();
|
semicolon();
|
||||||
}
|
}
|
||||||
return new AST_Return({
|
return new AST_Return({
|
||||||
@@ -905,7 +906,7 @@ function parse($TEXT, options) {
|
|||||||
next();
|
next();
|
||||||
if (has_newline_before(S.token))
|
if (has_newline_before(S.token))
|
||||||
croak("Illegal newline after 'throw'");
|
croak("Illegal newline after 'throw'");
|
||||||
var value = expression(true);
|
var value = expression();
|
||||||
semicolon();
|
semicolon();
|
||||||
return new AST_Throw({
|
return new AST_Throw({
|
||||||
value: value
|
value: value
|
||||||
@@ -956,9 +957,7 @@ function parse($TEXT, options) {
|
|||||||
// https://github.com/mishoo/UglifyJS/issues/287
|
// https://github.com/mishoo/UglifyJS/issues/287
|
||||||
label.references.forEach(function(ref) {
|
label.references.forEach(function(ref) {
|
||||||
if (ref instanceof AST_Continue) {
|
if (ref instanceof AST_Continue) {
|
||||||
ref = ref.label.start;
|
token_error(ref.label.start, "Continue label `" + label.name + "` must refer to IterationStatement");
|
||||||
croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
|
|
||||||
ref.line, ref.col, ref.pos);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -966,7 +965,7 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function simple_statement() {
|
function simple_statement() {
|
||||||
var body = expression(true);
|
var body = expression();
|
||||||
semicolon();
|
semicolon();
|
||||||
return new AST_SimpleStatement({ body: body });
|
return new AST_SimpleStatement({ body: body });
|
||||||
}
|
}
|
||||||
@@ -980,7 +979,7 @@ function parse($TEXT, options) {
|
|||||||
ldef = find_if(function(l) {
|
ldef = find_if(function(l) {
|
||||||
return l.name == label.name;
|
return l.name == label.name;
|
||||||
}, S.labels);
|
}, S.labels);
|
||||||
if (!ldef) croak("Undefined label " + label.name);
|
if (!ldef) token_error(label.start, "Undefined label " + label.name);
|
||||||
label.thedef = ldef;
|
label.thedef = ldef;
|
||||||
} else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
|
} else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
|
||||||
semicolon();
|
semicolon();
|
||||||
@@ -999,13 +998,14 @@ function parse($TEXT, options) {
|
|||||||
? (next(), let_(true))
|
? (next(), let_(true))
|
||||||
: is("keyword", "var")
|
: is("keyword", "var")
|
||||||
? (next(), var_(true))
|
? (next(), var_(true))
|
||||||
: expression(true, true);
|
: expression(true);
|
||||||
if (is("operator", "in")) {
|
if (is("operator", "in")) {
|
||||||
if (init instanceof AST_Definitions) {
|
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);
|
token_error(init.start, "Only one variable declaration allowed in for..in loop");
|
||||||
|
}
|
||||||
} else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
|
} 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);
|
token_error(init.start, "Invalid left-hand side in for..in loop");
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
return for_in(init);
|
return for_in(init);
|
||||||
@@ -1016,9 +1016,9 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function regular_for(init) {
|
function regular_for(init) {
|
||||||
expect(";");
|
expect(";");
|
||||||
var test = is("punc", ";") ? null : expression(true);
|
var test = is("punc", ";") ? null : expression();
|
||||||
expect(";");
|
expect(";");
|
||||||
var step = is("punc", ")") ? null : expression(true);
|
var step = is("punc", ")") ? null : expression();
|
||||||
expect(")");
|
expect(")");
|
||||||
return new AST_For({
|
return new AST_For({
|
||||||
init : init,
|
init : init,
|
||||||
@@ -1029,7 +1029,7 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function for_in(init) {
|
function for_in(init) {
|
||||||
var obj = expression(true);
|
var obj = expression();
|
||||||
expect(")");
|
expect(")");
|
||||||
return new AST_ForIn({
|
return new AST_ForIn({
|
||||||
init : init,
|
init : init,
|
||||||
@@ -1038,6 +1038,71 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function to_funarg(node) {
|
||||||
|
if (node instanceof AST_Array) return new AST_DestructuredArray({
|
||||||
|
start: node.start,
|
||||||
|
elements: node.elements.map(function(node) {
|
||||||
|
return node instanceof AST_Hole ? node : to_funarg(node);
|
||||||
|
}),
|
||||||
|
end: node.end,
|
||||||
|
});
|
||||||
|
if (node instanceof AST_Object) return new AST_DestructuredObject({
|
||||||
|
start: node.start,
|
||||||
|
properties: node.properties.map(function(prop) {
|
||||||
|
if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment");
|
||||||
|
return new AST_DestructuredKeyVal({
|
||||||
|
start: prop.start,
|
||||||
|
key: prop.key,
|
||||||
|
value: to_funarg(prop.value),
|
||||||
|
end: prop.end,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
end: node.end,
|
||||||
|
});
|
||||||
|
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
|
||||||
|
token_error(node.start, "Invalid arrow parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrow(exprs, start) {
|
||||||
|
var was_async = S.in_async;
|
||||||
|
S.in_async = false;
|
||||||
|
var was_funarg = S.in_funarg;
|
||||||
|
S.in_funarg = S.in_function;
|
||||||
|
var argnames = exprs.map(to_funarg);
|
||||||
|
S.in_funarg = was_funarg;
|
||||||
|
expect("=>");
|
||||||
|
var body, value;
|
||||||
|
var loop = S.in_loop;
|
||||||
|
var labels = S.labels;
|
||||||
|
++S.in_function;
|
||||||
|
S.in_directives = true;
|
||||||
|
S.input.push_directives_stack();
|
||||||
|
S.in_loop = 0;
|
||||||
|
S.labels = [];
|
||||||
|
if (is("punc", "{")) {
|
||||||
|
body = block_();
|
||||||
|
value = null;
|
||||||
|
if (S.input.has_directive("use strict")) {
|
||||||
|
argnames.forEach(strict_verify_symbol);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body = [];
|
||||||
|
value = maybe_assign();
|
||||||
|
}
|
||||||
|
S.input.pop_directives_stack();
|
||||||
|
--S.in_function;
|
||||||
|
S.in_loop = loop;
|
||||||
|
S.labels = labels;
|
||||||
|
S.in_async = was_async;
|
||||||
|
return new AST_Arrow({
|
||||||
|
start: start,
|
||||||
|
argnames: argnames,
|
||||||
|
body: body,
|
||||||
|
value: value,
|
||||||
|
end: prev(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var function_ = function(ctor) {
|
var function_ = function(ctor) {
|
||||||
var was_async = S.in_async;
|
var was_async = S.in_async;
|
||||||
var name;
|
var name;
|
||||||
@@ -1118,7 +1183,7 @@ function parse($TEXT, options) {
|
|||||||
cur = [];
|
cur = [];
|
||||||
branch = new AST_Case({
|
branch = new AST_Case({
|
||||||
start : (tmp = S.token, next(), tmp),
|
start : (tmp = S.token, next(), tmp),
|
||||||
expression : expression(true),
|
expression : expression(),
|
||||||
body : cur
|
body : cur
|
||||||
});
|
});
|
||||||
a.push(branch);
|
a.push(branch);
|
||||||
@@ -1151,7 +1216,7 @@ function parse($TEXT, options) {
|
|||||||
var name = null;
|
var name = null;
|
||||||
if (is("punc", "(")) {
|
if (is("punc", "(")) {
|
||||||
next();
|
next();
|
||||||
name = as_symbol(AST_SymbolCatch);
|
name = maybe_destructured(AST_SymbolCatch);
|
||||||
expect(")");
|
expect(")");
|
||||||
}
|
}
|
||||||
bcatch = new AST_Catch({
|
bcatch = new AST_Catch({
|
||||||
@@ -1187,7 +1252,7 @@ function parse($TEXT, options) {
|
|||||||
var value = null;
|
var value = null;
|
||||||
if (is("operator", "=")) {
|
if (is("operator", "=")) {
|
||||||
next();
|
next();
|
||||||
value = expression(false, no_in);
|
value = maybe_assign(no_in);
|
||||||
} else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
|
} else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
|
||||||
croak("Missing initializer in declaration");
|
croak("Missing initializer in declaration");
|
||||||
}
|
}
|
||||||
@@ -1251,9 +1316,6 @@ function parse($TEXT, options) {
|
|||||||
function as_atom_node() {
|
function as_atom_node() {
|
||||||
var tok = S.token, ret;
|
var tok = S.token, ret;
|
||||||
switch (tok.type) {
|
switch (tok.type) {
|
||||||
case "name":
|
|
||||||
ret = _make_symbol(AST_SymbolRef, tok);
|
|
||||||
break;
|
|
||||||
case "num":
|
case "num":
|
||||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||||
break;
|
break;
|
||||||
@@ -1295,7 +1357,11 @@ function parse($TEXT, options) {
|
|||||||
switch (start.value) {
|
switch (start.value) {
|
||||||
case "(":
|
case "(":
|
||||||
next();
|
next();
|
||||||
var ex = expression(true);
|
if (is("punc", ")")) {
|
||||||
|
next();
|
||||||
|
return arrow([], start);
|
||||||
|
}
|
||||||
|
var ex = expression(false, true);
|
||||||
var len = start.comments_before.length;
|
var len = start.comments_before.length;
|
||||||
[].unshift.apply(ex.start.comments_before, start.comments_before);
|
[].unshift.apply(ex.start.comments_before, start.comments_before);
|
||||||
start.comments_before.length = 0;
|
start.comments_before.length = 0;
|
||||||
@@ -1318,6 +1384,7 @@ function parse($TEXT, options) {
|
|||||||
end.comments_after = ex.end.comments_after;
|
end.comments_after = ex.end.comments_after;
|
||||||
ex.end = end;
|
ex.end = end;
|
||||||
if (ex instanceof AST_Call) mark_pure(ex);
|
if (ex instanceof AST_Call) mark_pure(ex);
|
||||||
|
if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start);
|
||||||
return subscripts(ex, allow_calls);
|
return subscripts(ex, allow_calls);
|
||||||
case "[":
|
case "[":
|
||||||
return subscripts(array_(), allow_calls);
|
return subscripts(array_(), allow_calls);
|
||||||
@@ -1340,6 +1407,11 @@ function parse($TEXT, options) {
|
|||||||
func.end = prev();
|
func.end = prev();
|
||||||
return subscripts(func, allow_calls);
|
return subscripts(func, allow_calls);
|
||||||
}
|
}
|
||||||
|
if (is("name")) {
|
||||||
|
var sym = _make_symbol(AST_SymbolRef, start);
|
||||||
|
next();
|
||||||
|
return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls);
|
||||||
|
}
|
||||||
if (ATOMIC_START_TOKEN[S.token.type]) {
|
if (ATOMIC_START_TOKEN[S.token.type]) {
|
||||||
return subscripts(as_atom_node(), allow_calls);
|
return subscripts(as_atom_node(), allow_calls);
|
||||||
}
|
}
|
||||||
@@ -1347,14 +1419,14 @@ function parse($TEXT, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
|
function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
|
||||||
if (!parser) parser = expression;
|
if (!parser) parser = maybe_assign;
|
||||||
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(",");
|
||||||
if (allow_trailing_comma && is("punc", closing)) break;
|
if (allow_trailing_comma && is("punc", closing)) break;
|
||||||
if (allow_empty && is("punc", ",")) {
|
if (allow_empty && is("punc", ",")) {
|
||||||
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||||
} else if (parser === expression && is("operator", "...")) {
|
} else if (parser === maybe_assign && is("operator", "...")) {
|
||||||
a.push(new AST_Spread({
|
a.push(new AST_Spread({
|
||||||
start: S.token,
|
start: S.token,
|
||||||
expression: (next(), parser()),
|
expression: (next(), parser()),
|
||||||
@@ -1391,7 +1463,7 @@ function parse($TEXT, options) {
|
|||||||
next();
|
next();
|
||||||
a.push(new AST_Spread({
|
a.push(new AST_Spread({
|
||||||
start: start,
|
start: start,
|
||||||
expression: expression(false),
|
expression: maybe_assign(),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
@@ -1410,7 +1482,39 @@ function parse($TEXT, options) {
|
|||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!is("punc", ":") && start.type == "name") switch (key) {
|
if (is("punc", ":")) {
|
||||||
|
next();
|
||||||
|
a.push(new AST_ObjectKeyVal({
|
||||||
|
start: start,
|
||||||
|
key: key,
|
||||||
|
value: maybe_assign(),
|
||||||
|
end: prev(),
|
||||||
|
}));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (is("punc", ",") || is("punc", "}")) {
|
||||||
|
a.push(new AST_ObjectKeyVal({
|
||||||
|
start: start,
|
||||||
|
key: key,
|
||||||
|
value: _make_symbol(AST_SymbolRef, start),
|
||||||
|
end: prev(),
|
||||||
|
}));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (start.type == "name") switch (key) {
|
||||||
|
case "async":
|
||||||
|
key = as_property_key();
|
||||||
|
var func_start = S.token;
|
||||||
|
var func = function_(AST_AsyncFunction);
|
||||||
|
func.start = func_start;
|
||||||
|
func.end = prev();
|
||||||
|
a.push(new AST_ObjectKeyVal({
|
||||||
|
start: start,
|
||||||
|
key: key,
|
||||||
|
value: func,
|
||||||
|
end: prev(),
|
||||||
|
}));
|
||||||
|
continue;
|
||||||
case "get":
|
case "get":
|
||||||
a.push(new AST_ObjectGetter({
|
a.push(new AST_ObjectGetter({
|
||||||
start: start,
|
start: start,
|
||||||
@@ -1427,22 +1531,8 @@ function parse($TEXT, options) {
|
|||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
default:
|
|
||||||
a.push(new AST_ObjectKeyVal({
|
|
||||||
start: start,
|
|
||||||
key: key,
|
|
||||||
value: _make_symbol(AST_SymbolRef, start),
|
|
||||||
end: prev(),
|
|
||||||
}));
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
expect(":");
|
unexpected();
|
||||||
a.push(new AST_ObjectKeyVal({
|
|
||||||
start: start,
|
|
||||||
key: key,
|
|
||||||
value: expression(false),
|
|
||||||
end: prev(),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
return new AST_Object({ properties: a });
|
return new AST_Object({ properties: a });
|
||||||
@@ -1461,9 +1551,8 @@ function parse($TEXT, options) {
|
|||||||
next();
|
next();
|
||||||
return "" + tmp.value;
|
return "" + tmp.value;
|
||||||
case "punc":
|
case "punc":
|
||||||
if (tmp.value != "[") unexpected();
|
expect("[");
|
||||||
next();
|
var key = maybe_assign();
|
||||||
var key = expression(false);
|
|
||||||
expect("]");
|
expect("]");
|
||||||
return key;
|
return key;
|
||||||
default:
|
default:
|
||||||
@@ -1490,7 +1579,7 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function strict_verify_symbol(sym) {
|
function strict_verify_symbol(sym) {
|
||||||
if (sym.name == "arguments" || sym.name == "eval")
|
if (sym.name == "arguments" || sym.name == "eval")
|
||||||
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
|
token_error(sym.start, "Unexpected " + sym.name + " in strict mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
function as_symbol(type, noerror) {
|
function as_symbol(type, noerror) {
|
||||||
@@ -1526,16 +1615,8 @@ function parse($TEXT, options) {
|
|||||||
// allow trailing comma
|
// allow trailing comma
|
||||||
if (!options.strict && is("punc", "}")) break;
|
if (!options.strict && is("punc", "}")) break;
|
||||||
var key_start = S.token;
|
var key_start = S.token;
|
||||||
|
if (is("punc", "[") || is_token(peek(), "punc", ":")) {
|
||||||
var key = as_property_key();
|
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(":");
|
expect(":");
|
||||||
a.push(new AST_DestructuredKeyVal({
|
a.push(new AST_DestructuredKeyVal({
|
||||||
start: key_start,
|
start: key_start,
|
||||||
@@ -1543,6 +1624,14 @@ function parse($TEXT, options) {
|
|||||||
value: maybe_destructured(type),
|
value: maybe_destructured(type),
|
||||||
end: prev(),
|
end: prev(),
|
||||||
}));
|
}));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
a.push(new AST_DestructuredKeyVal({
|
||||||
|
start: key_start,
|
||||||
|
key: key_start.value,
|
||||||
|
value: as_symbol(type),
|
||||||
|
end: prev(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
return new AST_DestructuredObject({
|
return new AST_DestructuredObject({
|
||||||
@@ -1580,7 +1669,7 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
if (is("punc", "[")) {
|
if (is("punc", "[")) {
|
||||||
next();
|
next();
|
||||||
var prop = expression(true);
|
var prop = expression();
|
||||||
expect("]");
|
expect("]");
|
||||||
return subscripts(new AST_Sub({
|
return subscripts(new AST_Sub({
|
||||||
start : start,
|
start : start,
|
||||||
@@ -1629,11 +1718,11 @@ function parse($TEXT, options) {
|
|||||||
case "++":
|
case "++":
|
||||||
case "--":
|
case "--":
|
||||||
if (!is_assignable(expr))
|
if (!is_assignable(expr))
|
||||||
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
|
token_error(token, "Invalid use of " + op + " operator");
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "delete":
|
||||||
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
|
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
|
||||||
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
|
token_error(expr.start, "Calling delete on expression not allowed in strict mode");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new ctor({ operator: op, expression: expr });
|
return new ctor({ operator: op, expression: expr });
|
||||||
@@ -1679,13 +1768,13 @@ function parse($TEXT, options) {
|
|||||||
var expr = expr_ops(no_in);
|
var expr = expr_ops(no_in);
|
||||||
if (is("operator", "?")) {
|
if (is("operator", "?")) {
|
||||||
next();
|
next();
|
||||||
var yes = expression(false);
|
var yes = maybe_assign();
|
||||||
expect(":");
|
expect(":");
|
||||||
return new AST_Conditional({
|
return new AST_Conditional({
|
||||||
start : start,
|
start : start,
|
||||||
condition : expr,
|
condition : expr,
|
||||||
consequent : yes,
|
consequent : yes,
|
||||||
alternative : expression(false, no_in),
|
alternative : maybe_assign(no_in),
|
||||||
end : prev()
|
end : prev()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1728,7 +1817,7 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var maybe_assign = function(no_in) {
|
function maybe_assign(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]) {
|
||||||
@@ -1745,23 +1834,23 @@ function parse($TEXT, options) {
|
|||||||
croak("Invalid assignment");
|
croak("Invalid assignment");
|
||||||
}
|
}
|
||||||
return left;
|
return left;
|
||||||
};
|
}
|
||||||
|
|
||||||
var expression = function(commas, no_in) {
|
function expression(no_in, maybe_arrow) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
var exprs = [];
|
var exprs = [];
|
||||||
while (true) {
|
while (true) {
|
||||||
exprs.push(maybe_assign(no_in));
|
exprs.push(maybe_assign(no_in));
|
||||||
if (!commas || !is("punc", ",")) break;
|
if (!is("punc", ",")) break;
|
||||||
next();
|
next();
|
||||||
commas = true;
|
if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break;
|
||||||
}
|
}
|
||||||
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
||||||
start : start,
|
start : start,
|
||||||
expressions : exprs,
|
expressions : exprs,
|
||||||
end : peek()
|
end : prev()
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function in_loop(cont) {
|
function in_loop(cont) {
|
||||||
++S.in_loop;
|
++S.in_loop;
|
||||||
@@ -1772,7 +1861,7 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
if (options.expression) {
|
if (options.expression) {
|
||||||
handle_regexp();
|
handle_regexp();
|
||||||
return expression(true);
|
return expression();
|
||||||
}
|
}
|
||||||
|
|
||||||
return function() {
|
return function() {
|
||||||
|
|||||||
35
lib/scope.js
35
lib/scope.js
@@ -100,6 +100,8 @@ SymbolDef.prototype = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var unary_side_effects = makePredicate("delete ++ --");
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||||
options = defaults(options, {
|
options = defaults(options, {
|
||||||
cache: null,
|
cache: null,
|
||||||
@@ -206,13 +208,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
self.globals = new Dictionary();
|
self.globals = new Dictionary();
|
||||||
var in_arg = [];
|
var in_arg = [];
|
||||||
var tw = new TreeWalker(function(node) {
|
var tw = new TreeWalker(function(node) {
|
||||||
|
if (node instanceof AST_Catch) {
|
||||||
|
if (!(node.argname instanceof AST_Destructured)) return;
|
||||||
|
in_arg.push(node);
|
||||||
|
node.argname.walk(tw);
|
||||||
|
in_arg.pop();
|
||||||
|
walk_body(node, tw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda) {
|
||||||
in_arg.push(node);
|
in_arg.push(node);
|
||||||
node.argnames.forEach(function(argname) {
|
node.argnames.forEach(function(argname) {
|
||||||
argname.walk(tw);
|
argname.walk(tw);
|
||||||
});
|
});
|
||||||
in_arg.pop();
|
in_arg.pop();
|
||||||
|
if (node instanceof AST_Arrow && node.value) {
|
||||||
|
node.value.walk(tw);
|
||||||
|
} else {
|
||||||
walk_body(node, tw);
|
walk_body(node, tw);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LoopControl) {
|
if (node instanceof AST_LoopControl) {
|
||||||
@@ -241,7 +255,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
i = in_arg.lastIndexOf(sym.scope, i - 1);
|
i = in_arg.lastIndexOf(sym.scope, i - 1);
|
||||||
if (i < 0) break;
|
if (i < 0) break;
|
||||||
var decl = sym.orig[0];
|
var decl = sym.orig[0];
|
||||||
if (decl instanceof AST_SymbolFunarg || decl instanceof AST_SymbolLambda) {
|
if (decl instanceof AST_SymbolCatch
|
||||||
|
|| decl instanceof AST_SymbolFunarg
|
||||||
|
|| decl instanceof AST_SymbolLambda) {
|
||||||
node.in_arg = true;
|
node.in_arg = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -249,9 +265,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
}
|
}
|
||||||
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"
|
||||||
if (!(tw.parent() instanceof AST_PropAccess)) {
|
&& sym.orig[0] instanceof AST_SymbolFunarg
|
||||||
sym.scope.uses_arguments = "d";
|
&& !(sym.orig[1] instanceof AST_SymbolFunarg)
|
||||||
|
&& !(sym.scope instanceof AST_Arrow)) {
|
||||||
|
var parent = tw.parent();
|
||||||
|
if (parent instanceof AST_Assign && parent.left === node
|
||||||
|
|| parent instanceof AST_Unary && unary_side_effects[parent.operator]) {
|
||||||
|
sym.scope.uses_arguments = 3;
|
||||||
|
} else if (sym.scope.uses_arguments < 2
|
||||||
|
&& !(parent instanceof AST_PropAccess && parent.expression === node)) {
|
||||||
|
sym.scope.uses_arguments = 2;
|
||||||
} else if (!sym.scope.uses_arguments) {
|
} else if (!sym.scope.uses_arguments) {
|
||||||
sym.scope.uses_arguments = true;
|
sym.scope.uses_arguments = true;
|
||||||
}
|
}
|
||||||
@@ -360,6 +384,9 @@ AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
|
|||||||
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
|
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
|
||||||
init_scope_vars(this, parent_scope);
|
init_scope_vars(this, parent_scope);
|
||||||
});
|
});
|
||||||
|
AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) {
|
||||||
|
init_scope_vars(this, parent_scope);
|
||||||
|
});
|
||||||
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
|
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
|
||||||
init_scope_vars(this, parent_scope);
|
init_scope_vars(this, parent_scope);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
|
|||||||
@@ -131,6 +131,14 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
self.argnames = do_list(self.argnames, tw);
|
self.argnames = do_list(self.argnames, tw);
|
||||||
self.body = do_list(self.body, tw);
|
self.body = do_list(self.body, tw);
|
||||||
});
|
});
|
||||||
|
DEF(AST_Arrow, function(self, tw) {
|
||||||
|
self.argnames = do_list(self.argnames, tw);
|
||||||
|
if (self.value) {
|
||||||
|
self.value = self.value.transform(tw);
|
||||||
|
} else {
|
||||||
|
self.body = do_list(self.body, tw);
|
||||||
|
}
|
||||||
|
});
|
||||||
DEF(AST_Call, function(self, tw) {
|
DEF(AST_Call, function(self, tw) {
|
||||||
self.expression = self.expression.transform(tw);
|
self.expression = self.expression.transform(tw);
|
||||||
self.args = do_list(self.args, tw);
|
self.args = do_list(self.args, tw);
|
||||||
|
|||||||
@@ -238,13 +238,15 @@ function HOP(obj, prop) {
|
|||||||
// return true if the node at the top of the stack (that means the
|
// return true if the node at the top of the stack (that means the
|
||||||
// innermost node in the current output) is lexically the first in
|
// innermost node in the current output) is lexically the first in
|
||||||
// a statement.
|
// a statement.
|
||||||
function first_in_statement(stack) {
|
function first_in_statement(stack, arrow) {
|
||||||
var node = stack.parent(-1);
|
var node = stack.parent(-1);
|
||||||
for (var i = 0, p; p = stack.parent(i++); node = p) {
|
for (var i = 0, p; p = stack.parent(i++); node = p) {
|
||||||
if (p.TYPE == "Call") {
|
if (p instanceof AST_Arrow) {
|
||||||
if (p.expression === node) continue;
|
return arrow && p.value === node;
|
||||||
} else if (p instanceof AST_Binary) {
|
} else if (p instanceof AST_Binary) {
|
||||||
if (p.left === node) continue;
|
if (p.left === node) continue;
|
||||||
|
} else if (p.TYPE == "Call") {
|
||||||
|
if (p.expression === node) continue;
|
||||||
} else if (p instanceof AST_Conditional) {
|
} else if (p instanceof AST_Conditional) {
|
||||||
if (p.condition === node) continue;
|
if (p.condition === node) continue;
|
||||||
} else if (p instanceof AST_PropAccess) {
|
} else if (p instanceof AST_PropAccess) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "3.12.2",
|
"version": "3.12.3",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -851,3 +851,114 @@ issue_4291_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS 42 1"
|
expect_stdout: "PASS 42 1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4397: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
keep_fargs: "strict",
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
arguments += 0;
|
||||||
|
return arguments[0];
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
arguments += 0;
|
||||||
|
return arguments[0];
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "string"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4410_1: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(a) {
|
||||||
|
console.log(arguments[0] === (a = 0) ? "FAIL" : "PASS");
|
||||||
|
})(1);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(a) {
|
||||||
|
console.log(a === (a = 0) ? "FAIL" : "PASS");
|
||||||
|
})(1);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4410_2: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
conditionals: true,
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function f(a) {
|
||||||
|
console.log(arguments[0] === (a = 0) ? "FAIL" : "PASS");
|
||||||
|
})(1);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function f(a) {
|
||||||
|
console.log(arguments[0] === (a = 0) ? "FAIL" : "PASS");
|
||||||
|
})(1);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4410_3: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
(function f(b) {
|
||||||
|
a-- && f();
|
||||||
|
for (var c = 2; c--;)
|
||||||
|
switch (arguments[0]) {
|
||||||
|
case b = 42:
|
||||||
|
case 42:
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
})(null);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
(function f(b) {
|
||||||
|
a-- && f();
|
||||||
|
for (var c = 2; c--;)
|
||||||
|
switch (arguments[0]) {
|
||||||
|
case b = 42:
|
||||||
|
case 42:
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
})(null);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4432: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
for (a in { FAIL: 42 });
|
||||||
|
return arguments[0];
|
||||||
|
}() || "PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
for (a in { FAIL: 42 });
|
||||||
|
return arguments[0];
|
||||||
|
}() || "PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
589
test/compress/arrows.js
Normal file
589
test/compress/arrows.js
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
no_funarg: {
|
||||||
|
input: {
|
||||||
|
(() => console.log(42))();
|
||||||
|
}
|
||||||
|
expect_exact: "(()=>console.log(42))();"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_funarg: {
|
||||||
|
input: {
|
||||||
|
(a => console.log(a))(42);
|
||||||
|
}
|
||||||
|
expect_exact: "(a=>console.log(a))(42);"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
multiple_funargs: {
|
||||||
|
input: {
|
||||||
|
((a, b) => console.log(a, b))("foo", "bar");
|
||||||
|
}
|
||||||
|
expect_exact: '((a,b)=>console.log(a,b))("foo","bar");'
|
||||||
|
expect_stdout: "foo bar"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
destructured_funarg: {
|
||||||
|
input: {
|
||||||
|
(([ a, b, c ]) => console.log(a, b, c))("foo");
|
||||||
|
}
|
||||||
|
expect_exact: '(([a,b,c])=>console.log(a,b,c))("foo");'
|
||||||
|
expect_stdout: "f o o"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
await_parenthesis: {
|
||||||
|
input: {
|
||||||
|
async function f() {
|
||||||
|
await (a => a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_exact: "async function f(){await(a=>a)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_parenthesis_init: {
|
||||||
|
input: {
|
||||||
|
for (a => (a in a); console.log(42););
|
||||||
|
}
|
||||||
|
expect_exact: "for((a=>a in a);console.log(42););"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_parenthesis_condition: {
|
||||||
|
input: {
|
||||||
|
for (console.log(42); a => (a in a);)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect_exact: "for(console.log(42);a=>a in a;)break;"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_parenthesis_step: {
|
||||||
|
input: {
|
||||||
|
for (; console.log(42); a => (a in a));
|
||||||
|
}
|
||||||
|
expect_exact: "for(;console.log(42);a=>a in a);"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_assign_parenthesis_init: {
|
||||||
|
input: {
|
||||||
|
for (f = a => (a in a); console.log(42););
|
||||||
|
}
|
||||||
|
expect_exact: "for((f=a=>a in a);console.log(42););"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_assign_parenthesis_condition: {
|
||||||
|
input: {
|
||||||
|
for (console.log(42); f = a => (a in a);)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
expect_exact: "for(console.log(42);f=a=>a in a;)break;"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_assign_parenthesis_step: {
|
||||||
|
input: {
|
||||||
|
for (; console.log(42); f = a => (a in a));
|
||||||
|
}
|
||||||
|
expect_exact: "for(;console.log(42);f=a=>a in a);"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_declaration_parenthesis_init: {
|
||||||
|
input: {
|
||||||
|
for (var f = a => (a in a); console.log(42););
|
||||||
|
}
|
||||||
|
expect_exact: "for(var f=(a=>a in a);console.log(42););"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
for_statement_parenthesis_init: {
|
||||||
|
input: {
|
||||||
|
for (a => {
|
||||||
|
a in a;
|
||||||
|
}; console.log(42););
|
||||||
|
}
|
||||||
|
expect_exact: "for(a=>{a in a};console.log(42););"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
body_call: {
|
||||||
|
input: {
|
||||||
|
(() => {
|
||||||
|
console.log("foo");
|
||||||
|
console.log("bar");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_exact: '(()=>{console.log("foo");console.log("bar")})();'
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
body_conditional: {
|
||||||
|
input: {
|
||||||
|
console.log((a => {}) ? "PASS" : "FAIL");
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log((a=>{})?"PASS":"FAIL");'
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
destructured_object_value: {
|
||||||
|
input: {
|
||||||
|
console.log((a => ({} = a))(42));
|
||||||
|
}
|
||||||
|
expect_exact: "console.log((a=>({}=a))(42));"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
function_value: {
|
||||||
|
input: {
|
||||||
|
console.log((a => function() {
|
||||||
|
return a;
|
||||||
|
})(42)());
|
||||||
|
}
|
||||||
|
expect_exact: "console.log((a=>function(){return a})(42)());"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
in_value: {
|
||||||
|
input: {
|
||||||
|
console.log((a => a in {
|
||||||
|
foo: 42,
|
||||||
|
})("foo"));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log((a=>a in{foo:42})("foo"));'
|
||||||
|
expect_stdout: "true"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
object_value: {
|
||||||
|
input: {
|
||||||
|
console.log((() => ({
|
||||||
|
4: 2,
|
||||||
|
}))()[4]);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log((()=>({4:2}))()[4]);"
|
||||||
|
expect_stdout: "2"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
object_first_in_value: {
|
||||||
|
input: {
|
||||||
|
console.log((a => ({
|
||||||
|
p: a,
|
||||||
|
}.p ? "FAIL" : "PASS"))());
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log((a=>({p:a}).p?"FAIL":"PASS")());'
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence_value: {
|
||||||
|
input: {
|
||||||
|
console.log((a => (console.log("foo"), a))("bar"));
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log((a=>(console.log("foo"),a))("bar"));'
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
side_effects_value: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log((a => function() {
|
||||||
|
return a;
|
||||||
|
})(42)());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((a => function() {
|
||||||
|
return a;
|
||||||
|
})(42)());
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrow_property: {
|
||||||
|
input: {
|
||||||
|
console.log((a => 42).prototype);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log((a=>42).prototype);"
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
assign_arrow: {
|
||||||
|
input: {
|
||||||
|
var f = a => a;
|
||||||
|
console.log(f(42));
|
||||||
|
}
|
||||||
|
expect_exact: "var f=a=>a;console.log(f(42));"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_arrow: {
|
||||||
|
input: {
|
||||||
|
console.log(4 || (() => 2));
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(4||(()=>2));"
|
||||||
|
expect_stdout: "4"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
unary_arrow: {
|
||||||
|
input: {
|
||||||
|
console.log(+(() => 42));
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(+(()=>42));"
|
||||||
|
expect_stdout: "NaN"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
trailing_comma: {
|
||||||
|
input: {
|
||||||
|
((a,) => console.log(a))(42);
|
||||||
|
}
|
||||||
|
expect_exact: "(a=>console.log(a))(42);"
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_arguments: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
keep_fargs: false,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
return () => arguments[0];
|
||||||
|
}("PASS")("FAIL"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(argument_0) {
|
||||||
|
return () => argument_0;
|
||||||
|
}("PASS")("FAIL"));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
funarg_arguments: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log((arguments => arguments)(42));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(42);
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline_arguments: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
return () => arguments[0];
|
||||||
|
}("PASS")("FAIL"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
return () => arguments[0];
|
||||||
|
}("PASS")("FAIL"));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
var_arguments: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
properties: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
return () => {
|
||||||
|
var arguments = [ "PASS" ];
|
||||||
|
return arguments;
|
||||||
|
};
|
||||||
|
}("FAIL 1")("FAIL 2")[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
negate: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
if (!console ? 0 : () => 1)
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(console ? () => 1 : 0) && console.log("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline_this: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = {
|
||||||
|
p: function() {
|
||||||
|
return function() {
|
||||||
|
return () => this.q;
|
||||||
|
}();
|
||||||
|
},
|
||||||
|
q: "FAIL",
|
||||||
|
};
|
||||||
|
q = "PASS";
|
||||||
|
console.log(o.p()());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = {
|
||||||
|
p: function() {
|
||||||
|
return function() {
|
||||||
|
return () => this.q;
|
||||||
|
}();
|
||||||
|
},
|
||||||
|
q: "FAIL",
|
||||||
|
};
|
||||||
|
q = "PASS";
|
||||||
|
console.log(o.p()());
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
trim_body: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
if_return: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var f = a => {
|
||||||
|
return a;
|
||||||
|
};
|
||||||
|
var g = b => void b;
|
||||||
|
console.log(f("PASS"), g("FAIL"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var f = a => a;
|
||||||
|
var g = b => {};
|
||||||
|
console.log(f("PASS"), g("FAIL"));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS undefined"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse_value: {
|
||||||
|
options = {
|
||||||
|
arrows: true,
|
||||||
|
collapse_vars: true,
|
||||||
|
keep_fargs: "strict",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 42;
|
||||||
|
console.log((b => Math.floor(b))(a));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 42;
|
||||||
|
console.log((() => Math.floor(a))());
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_iife_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
keep_fargs: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(a => console.log(a + a))(21);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(() => console.log(42))();
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_iife_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 21;
|
||||||
|
(() => console.log(a + a))();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(() => console.log(42))();
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_iife_3: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "foo";
|
||||||
|
(() => {
|
||||||
|
console.log(a);
|
||||||
|
console.log(a);
|
||||||
|
})();
|
||||||
|
a = "bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(() => {
|
||||||
|
console.log("foo");
|
||||||
|
console.log("foo");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
"foo",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_recursive: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
return (() => f)();
|
||||||
|
}
|
||||||
|
console.log(typeof f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(typeof function f() {
|
||||||
|
return (() => f)();
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "function"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4388: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(arguments => console.log(arguments && arguments))();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(arguments => console.log(arguments && arguments))();
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4390: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function log() {
|
||||||
|
console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
var a = 42, b = "FAIL";
|
||||||
|
b = "PASS";
|
||||||
|
(c => log(b, c))(a);
|
||||||
|
log(b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function log() {
|
||||||
|
console.log.apply(console, arguments);
|
||||||
|
}
|
||||||
|
var a = 42, b = "FAIL";
|
||||||
|
b = "PASS";
|
||||||
|
(c => log(b, c))(a);
|
||||||
|
log(b);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS 42",
|
||||||
|
"PASS",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4401: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
var a = (b => b(a))(console.log || a);
|
||||||
|
var c = console.log;
|
||||||
|
c && c(typeof b);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
var a = (b => b(a))(console.log || a);
|
||||||
|
var c = console.log;
|
||||||
|
c && c(typeof b);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"undefined",
|
||||||
|
"undefined",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
@@ -143,6 +143,27 @@ negate_iife: {
|
|||||||
node_version: ">=8"
|
node_version: ">=8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_function: {
|
||||||
|
options = {
|
||||||
|
properties: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
({
|
||||||
|
async f() {
|
||||||
|
console.log("PASS");
|
||||||
|
},
|
||||||
|
}).f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(async function() {
|
||||||
|
console.log("PASS");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
collapse_vars_1: {
|
collapse_vars_1: {
|
||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
@@ -329,6 +350,77 @@ property_access_expression: {
|
|||||||
node_version: ">=8"
|
node_version: ">=8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reduce_iife_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
keep_fargs: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(async function(a) {
|
||||||
|
console.log(a + a);
|
||||||
|
})(21);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(async function() {
|
||||||
|
console.log(42);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_iife_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 21;
|
||||||
|
(async function() {
|
||||||
|
console.log(a + a);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(async function() {
|
||||||
|
console.log(42);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
reduce_iife_3: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "foo";
|
||||||
|
(async function() {
|
||||||
|
console.log(a);
|
||||||
|
console.log(await a);
|
||||||
|
})();
|
||||||
|
a = "bar";
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "foo";
|
||||||
|
(async function() {
|
||||||
|
console.log(a);
|
||||||
|
console.log(await a);
|
||||||
|
})();
|
||||||
|
a = "bar";
|
||||||
|
}
|
||||||
|
expect_stdout: "foo"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
issue_4347_1: {
|
issue_4347_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
@@ -496,3 +588,55 @@ issue_4377: {
|
|||||||
expect_stdout: "function"
|
expect_stdout: "function"
|
||||||
node_version: ">=8"
|
node_version: ">=8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4406: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
A = "PASS";
|
||||||
|
B = "FAIL";
|
||||||
|
(function() {
|
||||||
|
var a, b;
|
||||||
|
a = A;
|
||||||
|
(async function({
|
||||||
|
[console.log(a)]: {},
|
||||||
|
}) {})((b = B) && { undefined: b });
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
A = "PASS";
|
||||||
|
B = "FAIL";
|
||||||
|
(function() {
|
||||||
|
var a, b;
|
||||||
|
a = A;
|
||||||
|
(async function({
|
||||||
|
[console.log(a)]: {},
|
||||||
|
}) {})((b = B) && { undefined: b });
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4417: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(async function() {
|
||||||
|
console.log(function() {
|
||||||
|
return await => 0;
|
||||||
|
}().prototype);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(async function() {
|
||||||
|
console.log(function() {
|
||||||
|
return await => 0;
|
||||||
|
}().prototype);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|||||||
@@ -8602,3 +8602,63 @@ issue_4248: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "1"
|
expect_stdout: "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4430_1: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
switch (a = 1, arguments[0]) {
|
||||||
|
case 1:
|
||||||
|
return "PASS";
|
||||||
|
case 2:
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(f(2));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
switch (a = 1, arguments[0]) {
|
||||||
|
case 1:
|
||||||
|
return "PASS";
|
||||||
|
case 2:
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(f(2));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4430_2: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
switch (a = 0, arguments[0]) {
|
||||||
|
case 0:
|
||||||
|
return "PASS";
|
||||||
|
case 1:
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(f(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
switch (arguments[a = 0]) {
|
||||||
|
case 0:
|
||||||
|
return "PASS";
|
||||||
|
case 1:
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(f(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -750,6 +750,46 @@ simple_var: {
|
|||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop_catch: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {} catch ({
|
||||||
|
[console.log("FAIL")]: e,
|
||||||
|
}) {} finally {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_catch_var: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
throw new Error("PASS");
|
||||||
|
} catch ({ name, message }) {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
throw new Error("PASS");
|
||||||
|
} catch ({ message }) {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
collapse_vars_1: {
|
collapse_vars_1: {
|
||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
@@ -1844,3 +1884,156 @@ issue_4372_2: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4383: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
[ a[0] ] = [];
|
||||||
|
return a.length;
|
||||||
|
}([]));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
[ a[0] ] = [];
|
||||||
|
return a.length;
|
||||||
|
}([]));
|
||||||
|
}
|
||||||
|
expect_stdout: "1"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4386: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f({}) {
|
||||||
|
return arguments[0];
|
||||||
|
}
|
||||||
|
console.log(f("PASS"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f({}) {
|
||||||
|
return arguments[0];
|
||||||
|
}
|
||||||
|
console.log(f("PASS"));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4395: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a, {}) {
|
||||||
|
a = "FAIL";
|
||||||
|
return arguments[0];
|
||||||
|
}("PASS", 42));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a, {}) {
|
||||||
|
a = "FAIL";
|
||||||
|
return arguments[0];
|
||||||
|
}("PASS", 42));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4399: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function({
|
||||||
|
[arguments[1]]: a,
|
||||||
|
}, b) {
|
||||||
|
return a;
|
||||||
|
}([ "PASS" ], 0));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function({
|
||||||
|
[arguments[1]]: a,
|
||||||
|
}, b) {
|
||||||
|
return a;
|
||||||
|
}([ "PASS" ], 0));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4420: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw [ "FAIL", "PASS" ];
|
||||||
|
} catch ({
|
||||||
|
[a]: b,
|
||||||
|
}) {
|
||||||
|
let a = 0;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw [ "FAIL", "PASS" ];
|
||||||
|
} catch ({
|
||||||
|
[a]: b,
|
||||||
|
}) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4425: {
|
||||||
|
rename = true
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
console.log(function() {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch ({
|
||||||
|
[a]: a,
|
||||||
|
}) {}
|
||||||
|
return "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
console.log(function() {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch ({
|
||||||
|
[b]: b,
|
||||||
|
}) {}
|
||||||
|
return "FAIL";
|
||||||
|
} catch (c) {
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|||||||
@@ -3112,3 +3112,44 @@ issue_4235: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "undefined"
|
expect_stdout: "undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4404: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
arguments[0] = "PASS";
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
f("FAIL");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
arguments[0] = "PASS";
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
f("FAIL");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4413: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function f(arguments) {
|
||||||
|
var arguments = function() {};
|
||||||
|
return arguments.length;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(arguments) {
|
||||||
|
return function() {}.length;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "0"
|
||||||
|
}
|
||||||
|
|||||||
@@ -3074,3 +3074,46 @@ issue_4271: {
|
|||||||
"PASS",
|
"PASS",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4393: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function f(a) {
|
||||||
|
a = "PASS";
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})("FAIL");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function f(a) {
|
||||||
|
a = "PASS";
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})("FAIL");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4422: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function f(a) {
|
||||||
|
a = "FAIL 1";
|
||||||
|
arguments[0] = "PASS";
|
||||||
|
return a;
|
||||||
|
}("FAIL 2"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
a = "FAIL 1";
|
||||||
|
arguments[0] = "PASS";
|
||||||
|
return a;
|
||||||
|
}("FAIL 2"));
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -257,6 +257,29 @@ keep_computed_key: {
|
|||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shorthand_keywords: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unsafe: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var async = 1, get = 2, set = 3, o = {
|
||||||
|
async,
|
||||||
|
get,
|
||||||
|
set,
|
||||||
|
};
|
||||||
|
console.log(o.async, o.get, o.set);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(1, 2, 3);
|
||||||
|
}
|
||||||
|
expect_stdout: "1 2 3"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
issue_4269_1: {
|
issue_4269_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
@@ -413,3 +436,22 @@ issue_4380: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4415: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
objects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log({
|
||||||
|
["00"]: "FAIL",
|
||||||
|
}[0] || "PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log({
|
||||||
|
"00": "FAIL",
|
||||||
|
}[0] || "PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|||||||
8
test/input/invalid/destructured_var.js
Normal file
8
test/input/invalid/destructured_var.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
function f() {
|
||||||
|
var { eval } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
"use strict";
|
||||||
|
var { eval } = 42;
|
||||||
|
}
|
||||||
7
test/input/reduce/destructured_catch.js
Normal file
7
test/input/reduce/destructured_catch.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
try {
|
||||||
|
"foo" in 42;
|
||||||
|
} catch ({
|
||||||
|
message,
|
||||||
|
}) {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
15
test/input/reduce/destructured_catch.reduced.js
Normal file
15
test/input/reduce/destructured_catch.reduced.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// (beautified)
|
||||||
|
try {
|
||||||
|
1 in 0;
|
||||||
|
} catch ({
|
||||||
|
message: message
|
||||||
|
}) {
|
||||||
|
console.log(message);
|
||||||
|
}
|
||||||
|
// output: Cannot use 'in' operator to search for '1' in 0
|
||||||
|
//
|
||||||
|
// minify: Cannot use 'in' operator to search for '0' in 0
|
||||||
|
//
|
||||||
|
// options: {
|
||||||
|
// "mangle": false
|
||||||
|
// }
|
||||||
@@ -573,6 +573,20 @@ describe("bin/uglifyjs", function() {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("Should throw syntax error (var { eval })", function(done) {
|
||||||
|
var command = uglifyjscmd + " test/input/invalid/destructured_var.js";
|
||||||
|
exec(command, function(err, stdout, stderr) {
|
||||||
|
assert.ok(err);
|
||||||
|
assert.strictEqual(stdout, "");
|
||||||
|
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||||
|
"Parse error at test/input/invalid/destructured_var.js:7,10",
|
||||||
|
" var { eval } = 42;",
|
||||||
|
" ^",
|
||||||
|
"ERROR: Unexpected eval in strict mode"
|
||||||
|
].join("\n"));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
it("Should throw syntax error (else)", function(done) {
|
it("Should throw syntax error (else)", function(done) {
|
||||||
var command = uglifyjscmd + " test/input/invalid/else.js";
|
var command = uglifyjscmd + " test/input/invalid/else.js";
|
||||||
exec(command, function(err, stdout, stderr) {
|
exec(command, function(err, stdout, stderr) {
|
||||||
|
|||||||
@@ -313,4 +313,12 @@ describe("test/reduce.js", function() {
|
|||||||
if (result.error) throw result.error;
|
if (result.error) throw result.error;
|
||||||
assert.strictEqual(result.code, read("test/input/reduce/diff_error.reduced.js"));
|
assert.strictEqual(result.code, read("test/input/reduce/diff_error.reduced.js"));
|
||||||
});
|
});
|
||||||
|
it("Should handle destructured catch expressions", function() {
|
||||||
|
if (semver.satisfies(process.version, "<6")) return;
|
||||||
|
var result = reduce_test(read("test/input/reduce/destructured_catch.js"), {
|
||||||
|
mangle: false,
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code, read("test/input/reduce/destructured_catch.reduced.js"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -175,6 +175,12 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
return expr instanceof U.AST_Spread ? expr.expression : expr;
|
return expr instanceof U.AST_Spread ? expr.expression : expr;
|
||||||
}
|
}
|
||||||
|
if (node.expression instanceof U.AST_Arrow && node.expression.value) {
|
||||||
|
var seq = node.args.slice();
|
||||||
|
seq.push(node.expression.value);
|
||||||
|
CHANGED = true;
|
||||||
|
return to_sequence(seq);
|
||||||
|
}
|
||||||
if (node.expression instanceof U.AST_Function) {
|
if (node.expression instanceof U.AST_Function) {
|
||||||
// hoist and return expressions from the IIFE function expression
|
// hoist and return expressions from the IIFE function expression
|
||||||
var body = node.expression.body;
|
var body = node.expression.body;
|
||||||
@@ -459,7 +465,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
|
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
|
||||||
return to_sequence(node.args);
|
return to_sequence(node.args);
|
||||||
}
|
}
|
||||||
if (node instanceof U.AST_Catch && node.argname) {
|
if (node instanceof U.AST_Catch && node.argname instanceof U.AST_SymbolCatch) {
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
node.body.unshift(new U.AST_SimpleStatement({
|
node.body.unshift(new U.AST_SimpleStatement({
|
||||||
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
|
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
|
||||||
@@ -629,7 +635,8 @@ function is_timed_out(result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function is_statement(node) {
|
function is_statement(node) {
|
||||||
return node instanceof U.AST_Statement && !(node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function);
|
return node instanceof U.AST_Statement
|
||||||
|
&& !(node instanceof U.AST_Arrow || node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function);
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge_sequence(array, node) {
|
function merge_sequence(array, node) {
|
||||||
|
|||||||
@@ -132,9 +132,11 @@ var SUPPORT = function(matrix) {
|
|||||||
}
|
}
|
||||||
return matrix;
|
return matrix;
|
||||||
}({
|
}({
|
||||||
|
arrow: "a => 0;",
|
||||||
async: "async function f(){}",
|
async: "async function f(){}",
|
||||||
catch_omit_var: "try {} catch {}",
|
catch_omit_var: "try {} catch {}",
|
||||||
computed_key: "({[0]: 0});",
|
computed_key: "({[0]: 0});",
|
||||||
|
const_block: "var a; { const a = 0; }",
|
||||||
destructuring: "[] = [];",
|
destructuring: "[] = [];",
|
||||||
let: "let a;",
|
let: "let a;",
|
||||||
spread: "[...[]];",
|
spread: "[...[]];",
|
||||||
@@ -361,7 +363,7 @@ function createTopLevelCode() {
|
|||||||
return [
|
return [
|
||||||
strictMode(),
|
strictMode(),
|
||||||
"var _calls_ = 10, a = 100, b = 10, c = 0;",
|
"var _calls_ = 10, a = 100, b = 10, c = 0;",
|
||||||
rng(2) == 0
|
rng(2)
|
||||||
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
|
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
|
||||||
: createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
|
: createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
|
||||||
// preceding `null` makes for a cleaner output (empty string still shows up etc)
|
// preceding `null` makes for a cleaner output (empty string still shows up etc)
|
||||||
@@ -382,7 +384,9 @@ function addTrailingComma(list) {
|
|||||||
return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
|
return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createParams(noDuplicate) {
|
function createParams(was_async, noDuplicate) {
|
||||||
|
var save_async = async;
|
||||||
|
if (was_async) async = true;
|
||||||
var len = unique_vars.length;
|
var len = unique_vars.length;
|
||||||
var params = [];
|
var params = [];
|
||||||
for (var n = rng(4); --n >= 0;) {
|
for (var n = rng(4); --n >= 0;) {
|
||||||
@@ -391,6 +395,7 @@ function createParams(noDuplicate) {
|
|||||||
params.push(name);
|
params.push(name);
|
||||||
}
|
}
|
||||||
unique_vars.length = len;
|
unique_vars.length = len;
|
||||||
|
async = save_async;
|
||||||
return addTrailingComma(params.join(", "));
|
return addTrailingComma(params.join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,13 +416,13 @@ function createArgs(recurmax, stmtDepth, canThrow) {
|
|||||||
args.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
|
args.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
args.push(rng(2) ? createValue() : createExpression(recurmax, COMMA_OK, stmtDepth, canThrow));
|
args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return addTrailingComma(args.join(", "));
|
return addTrailingComma(args.join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, was_async) {
|
function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) {
|
||||||
var avoid = [];
|
var avoid = [];
|
||||||
var len = unique_vars.length;
|
var len = unique_vars.length;
|
||||||
var pairs = createPairs(recurmax);
|
var pairs = createPairs(recurmax);
|
||||||
@@ -425,42 +430,46 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
|
|||||||
return pairs;
|
return pairs;
|
||||||
|
|
||||||
function createAssignmentValue(recurmax) {
|
function createAssignmentValue(recurmax) {
|
||||||
var current = VAR_NAMES;
|
|
||||||
VAR_NAMES = (varNames || VAR_NAMES).slice();
|
|
||||||
var save_async = async;
|
var save_async = async;
|
||||||
if (was_async != null) async = was_async;
|
if (was_async != null) async = was_async;
|
||||||
var value = varNames && rng(2) ? createValue() : createExpression(recurmax, noComma, stmtDepth, canThrow);
|
var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
|
||||||
|
var value = nameLenBefore && rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
|
||||||
|
if (save_vars) [].push.apply(VAR_NAMES, save_vars);
|
||||||
async = save_async;
|
async = save_async;
|
||||||
VAR_NAMES = current;
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createKey(recurmax, keys) {
|
function createKey(recurmax, keys) {
|
||||||
addAvoidVars(avoid);
|
|
||||||
var key;
|
var key;
|
||||||
do {
|
do {
|
||||||
key = createObjectKey(recurmax, stmtDepth, canThrow);
|
key = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
} while (keys.indexOf(key) >= 0);
|
} while (keys.indexOf(key) >= 0);
|
||||||
removeAvoidVars(avoid);
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createName() {
|
||||||
|
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||||
|
var save_async = async;
|
||||||
|
if (was_async) async = true;
|
||||||
|
var name = createVarName(MANDATORY);
|
||||||
|
async = save_async;
|
||||||
|
unique_vars.length -= 6;
|
||||||
|
avoid.push(name);
|
||||||
|
unique_vars.push(name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
function createPairs(recurmax) {
|
function createPairs(recurmax) {
|
||||||
var names = [], values = [];
|
var names = [], values = [];
|
||||||
var m = rng(4), n = rng(4);
|
var m = rng(4), n = rng(4);
|
||||||
if (!varNames) m = Math.max(m, n, 1);
|
if (!nameLenBefore) m = Math.max(m, n, 1);
|
||||||
for (var i = Math.max(m, n); --i >= 0;) {
|
for (var i = Math.max(m, n); --i >= 0;) {
|
||||||
if (i < m && i < n) {
|
if (i < m && i < n) {
|
||||||
createDestructured(recurmax, names, values);
|
createDestructured(recurmax, names, values);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i < m) {
|
if (i < m) {
|
||||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
names.unshift(createName());
|
||||||
var name = createVarName(MANDATORY);
|
|
||||||
unique_vars.length -= 6;
|
|
||||||
avoid.push(name);
|
|
||||||
unique_vars.push(name);
|
|
||||||
names.unshift(name);
|
|
||||||
}
|
}
|
||||||
if (i < n) {
|
if (i < n) {
|
||||||
values.unshift(createAssignmentValue(recurmax));
|
values.unshift(createAssignmentValue(recurmax));
|
||||||
@@ -512,26 +521,32 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
|
|||||||
keys[index] = key;
|
keys[index] = key;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
var save_async = async;
|
||||||
|
if (was_async != null) {
|
||||||
|
async = false;
|
||||||
|
if (save_async || was_async) avoid.push("await");
|
||||||
|
}
|
||||||
|
addAvoidVars(avoid);
|
||||||
|
var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
|
||||||
names.unshift("{ " + addTrailingComma(pairs.names.map(function(name, index) {
|
names.unshift("{ " + addTrailingComma(pairs.names.map(function(name, index) {
|
||||||
var key = index in keys ? keys[index] : rng(10) && createKey(recurmax, keys);
|
var key = index in keys ? keys[index] : rng(10) && createKey(recurmax, keys);
|
||||||
return key ? key + ": " + name : name;
|
return key ? key + ": " + name : name;
|
||||||
}).join(", ")) + " }");
|
}).join(", ")) + " }");
|
||||||
var save_async = async;
|
if (was_async != null) {
|
||||||
if (was_async != null) async = was_async;
|
async = was_async;
|
||||||
|
if (save_async || was_async) removeAvoidVars([ avoid.pop() ]);
|
||||||
|
}
|
||||||
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) {
|
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) {
|
||||||
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
||||||
return key + ": " + value;
|
return key + ": " + value;
|
||||||
}).join(", ")) + " }");
|
}).join(", ")) + " }");
|
||||||
|
if (save_vars) [].push.apply(VAR_NAMES, save_vars);
|
||||||
|
removeAvoidVars(avoid);
|
||||||
async = save_async;
|
async = save_async;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
names.unshift(createName());
|
||||||
var name = createVarName(MANDATORY);
|
|
||||||
unique_vars.length -= 6;
|
|
||||||
avoid.push(name);
|
|
||||||
unique_vars.push(name);
|
|
||||||
names.unshift(name);
|
|
||||||
values.unshift(createAssignmentValue(recurmax));
|
values.unshift(createAssignmentValue(recurmax));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -545,12 +560,12 @@ function filterDirective(s) {
|
|||||||
|
|
||||||
function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||||
var block_len = block_vars.length;
|
var block_len = block_vars.length;
|
||||||
var var_len = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
var consts = [];
|
var consts = [];
|
||||||
var lets = [];
|
var lets = [];
|
||||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||||
while (!rng(block_vars.length > block_len ? 10 : 100)) {
|
while (!rng(block_vars.length > block_len ? 10 : 100)) {
|
||||||
var name = createVarName(MANDATORY, DONT_STORE);
|
var name = createVarName(MANDATORY);
|
||||||
if (SUPPORT.let && rng(2)) {
|
if (SUPPORT.let && rng(2)) {
|
||||||
lets.push(name);
|
lets.push(name);
|
||||||
} else {
|
} else {
|
||||||
@@ -568,8 +583,8 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
|||||||
return createDefinitions("let", lets) + "\n" + createDefinitions("const", consts) + "\n";
|
return createDefinitions("let", lets) + "\n" + createDefinitions("const", consts) + "\n";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
VAR_NAMES.length = nameLenBefore;
|
||||||
block_vars.length = block_len;
|
block_vars.length = block_len;
|
||||||
if (consts.length || lets.length) VAR_NAMES.splice(var_len, consts.length + lets.length);
|
|
||||||
|
|
||||||
function createDefinitions(type, names) {
|
function createDefinitions(type, names) {
|
||||||
if (!names.length) return "";
|
if (!names.length) return "";
|
||||||
@@ -602,6 +617,7 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
|||||||
removeAvoidVars([ name ]);
|
removeAvoidVars([ name ]);
|
||||||
return name + " = " + value;
|
return name + " = " + value;
|
||||||
}).join(", ") + ";";
|
}).join(", ") + ";";
|
||||||
|
names.length = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
removeAvoidVars(names);
|
removeAvoidVars(names);
|
||||||
@@ -609,6 +625,16 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function mayCreateBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||||
|
if (SUPPORT.const_block) {
|
||||||
|
createBlockVariables(recurmax, stmtDepth, canThrow, fn);
|
||||||
|
} else {
|
||||||
|
fn(function() {
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function makeFunction(name) {
|
function makeFunction(name) {
|
||||||
return (async ? "async function " : "function ") + name;
|
return (async ? "async function " : "function ") + name;
|
||||||
}
|
}
|
||||||
@@ -618,7 +644,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
||||||
var s = [];
|
var s = [];
|
||||||
var name, args;
|
var name, args;
|
||||||
var varNames = VAR_NAMES.slice();
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
var save_async = async;
|
var save_async = async;
|
||||||
async = SUPPORT.async && rng(50) == 0;
|
async = SUPPORT.async && rng(50) == 0;
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
@@ -632,11 +658,11 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
var params;
|
var params;
|
||||||
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
|
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
|
||||||
called[name] = false;
|
called[name] = false;
|
||||||
var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, save_async);
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||||
params = addTrailingComma(pairs.names.join(", "));
|
params = addTrailingComma(pairs.names.join(", "));
|
||||||
args = addTrailingComma(pairs.values.join(", "));
|
args = addTrailingComma(pairs.values.join(", "));
|
||||||
} else {
|
} else {
|
||||||
params = createParams();
|
params = createParams(save_async);
|
||||||
}
|
}
|
||||||
s.push(makeFunction(name) + "(" + params + "){", strictMode());
|
s.push(makeFunction(name) + "(" + params + "){", strictMode());
|
||||||
s.push(defns());
|
s.push(defns());
|
||||||
@@ -651,7 +677,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
s = filterDirective(s).join("\n");
|
s = filterDirective(s).join("\n");
|
||||||
});
|
});
|
||||||
async = save_async;
|
async = save_async;
|
||||||
VAR_NAMES = varNames;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
|
|
||||||
if (!allowDefun) {
|
if (!allowDefun) {
|
||||||
// avoid "function statements" (decl inside statements)
|
// avoid "function statements" (decl inside statements)
|
||||||
@@ -676,7 +702,7 @@ function _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotR
|
|||||||
|
|
||||||
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||||
var s = "";
|
var s = "";
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
s += defns() + "\n";
|
s += defns() + "\n";
|
||||||
s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
||||||
});
|
});
|
||||||
@@ -736,7 +762,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
var label = createLabel(canBreak);
|
var label = createLabel(canBreak);
|
||||||
return label.target + "{" + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}";
|
return label.target + "{" + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}";
|
||||||
case STMT_IF_ELSE:
|
case STMT_IF_ELSE:
|
||||||
return "if (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) === 1 ? " else " + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : "");
|
return "if (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) ? " else " + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : "");
|
||||||
case STMT_DO_WHILE:
|
case STMT_DO_WHILE:
|
||||||
var label = createLabel(canBreak, canContinue);
|
var label = createLabel(canBreak, canContinue);
|
||||||
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
@@ -777,7 +803,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
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:
|
||||||
if (SUPPORT.destructuring && rng(20) == 0) {
|
if (SUPPORT.destructuring && rng(20) == 0) {
|
||||||
var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow);
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow);
|
||||||
return "var " + pairs.names.map(function(name, index) {
|
return "var " + pairs.names.map(function(name, index) {
|
||||||
return index in pairs.values ? name + " = " + pairs.values[index] : name;
|
return index in pairs.values ? name + " = " + pairs.values[index] : name;
|
||||||
}).join(", ") + ";";
|
}).join(", ") + ";";
|
||||||
@@ -836,22 +862,41 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
if (n !== 1) {
|
if (n !== 1) {
|
||||||
// the catch var should only be accessible in the catch clause...
|
// the catch var should only be accessible in the catch clause...
|
||||||
// we have to do go through some trouble here to prevent leaking it
|
// we have to do go through some trouble here to prevent leaking it
|
||||||
|
mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
|
var block_len = block_vars.length;
|
||||||
var nameLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
var unique_len = unique_vars.length;
|
||||||
if (SUPPORT.catch_omit_var && rng(20) == 0) {
|
if (SUPPORT.catch_omit_var && !rng(20)) {
|
||||||
s += " catch { ";
|
s += " catch { ";
|
||||||
|
} else if (canThrow && SUPPORT.destructuring && !rng(20)) {
|
||||||
|
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||||
|
var name = createVarName(MANDATORY);
|
||||||
|
block_vars.push(name);
|
||||||
|
var message = createVarName(MANDATORY);
|
||||||
|
block_vars.push(message);
|
||||||
|
unique_vars.length -= 6;
|
||||||
|
if (SUPPORT.computed_key && rng(10) == 0) {
|
||||||
|
s += " catch ({ message: " + message + ", ";
|
||||||
|
addAvoidVars([ name ]);
|
||||||
|
s += "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]: " + name;
|
||||||
|
removeAvoidVars([ name ]);
|
||||||
|
s += " }) { ";
|
||||||
} else {
|
} else {
|
||||||
var catchName = createVarName(MANDATORY);
|
s += " catch ({ name: " + name + ", message: " + message + " }) { ";
|
||||||
if (!catch_redef) unique_vars.push(catchName);
|
|
||||||
s += " catch (" + catchName + ") { ";
|
|
||||||
}
|
}
|
||||||
var freshCatchName = VAR_NAMES.length !== nameLenBefore;
|
} else {
|
||||||
|
var name = createVarName(MANDATORY);
|
||||||
|
if (!catch_redef) unique_vars.push(name);
|
||||||
|
s += " catch (" + name + ") { ";
|
||||||
|
}
|
||||||
|
var catches = VAR_NAMES.length - nameLenBefore;
|
||||||
s += defns() + "\n";
|
s += defns() + "\n";
|
||||||
s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
||||||
s += " }";
|
s += " }";
|
||||||
// remove catch name
|
// remove catch variables
|
||||||
if (!catch_redef) unique_vars.pop();
|
block_vars.length = block_len;
|
||||||
if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
|
if (catches > 0) VAR_NAMES.splice(nameLenBefore, catches);
|
||||||
|
unique_vars.length = unique_len;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
|
if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
|
||||||
@@ -911,10 +956,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
switch (rng(_createExpression.N)) {
|
switch (rng(_createExpression.N)) {
|
||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
return createUnaryPrefix() + (rng(2) === 1 ? "a" : "b");
|
return createUnaryPrefix() + (rng(2) ? "a" : "b");
|
||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
return (rng(2) === 1 ? "a" : "b") + createUnaryPostfix();
|
return (rng(2) ? "a" : "b") + createUnaryPostfix();
|
||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
||||||
@@ -966,12 +1011,56 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
var s = [];
|
var s = [];
|
||||||
switch (rng(5)) {
|
switch (rng(5)) {
|
||||||
case 0:
|
case 0:
|
||||||
|
if (SUPPORT.arrow && !async && !name && rng(2)) {
|
||||||
|
var args, suffix;
|
||||||
|
(rng(2) ? createBlockVariables : function() {
|
||||||
|
arguments[3]();
|
||||||
|
})(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
|
var params;
|
||||||
|
if (SUPPORT.destructuring && rng(2)) {
|
||||||
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||||
|
params = addTrailingComma(pairs.names.join(", "));
|
||||||
|
args = addTrailingComma(pairs.values.join(", "));
|
||||||
|
} else {
|
||||||
|
params = createParams(save_async, NO_DUPLICATE);
|
||||||
|
}
|
||||||
|
if (defns) {
|
||||||
|
s.push(
|
||||||
|
"((" + params + ") => {",
|
||||||
|
strictMode(),
|
||||||
|
defns(),
|
||||||
|
_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)
|
||||||
|
);
|
||||||
|
suffix = "})";
|
||||||
|
} else {
|
||||||
|
s.push("((" + params + ") => ");
|
||||||
|
switch (rng(10)) {
|
||||||
|
case 0:
|
||||||
|
s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
s.push("(this && this." + getDotKey() + ")");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
s.push(createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
suffix = ")";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
async = save_async;
|
||||||
|
VAR_NAMES.length = nameLenBefore;
|
||||||
|
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
|
||||||
|
if (args) suffix += "(" + args + ")";
|
||||||
|
s.push(suffix);
|
||||||
|
} else {
|
||||||
s.push(
|
s.push(
|
||||||
"(" + makeFunction(name) + "(){",
|
"(" + makeFunction(name) + "(){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
rng(2) == 0 ? "})" : "})()"
|
rng(2) ? "})" : "})()"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
s.push(
|
s.push(
|
||||||
@@ -1002,7 +1091,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
var instantiate = rng(4) ? "new " : "";
|
var instantiate = rng(4) ? "new " : "";
|
||||||
s.push(
|
s.push(
|
||||||
instantiate + "function " + name + "(" + createParams() + "){",
|
instantiate + "function " + name + "(" + createParams(save_async) + "){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
defns()
|
defns()
|
||||||
);
|
);
|
||||||
@@ -1014,7 +1103,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
});
|
});
|
||||||
async = save_async;
|
async = save_async;
|
||||||
VAR_NAMES.length = nameLenBefore;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
s.push(rng(2) == 0 ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")");
|
s.push(rng(2) ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
async = save_async;
|
async = save_async;
|
||||||
@@ -1175,13 +1264,16 @@ function createObjectKey(recurmax, stmtDepth, canThrow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||||
var namesLenBefore = VAR_NAMES.length;
|
var nameLenBefore = VAR_NAMES.length;
|
||||||
|
var save_async = async;
|
||||||
var s;
|
var s;
|
||||||
|
var name = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
switch (rng(SUPPORT.computed_key ? 3 : 2)) {
|
switch (rng(SUPPORT.computed_key ? 3 : 2)) {
|
||||||
case 0:
|
case 0:
|
||||||
|
async = false;
|
||||||
s = [
|
s = [
|
||||||
"get " + createObjectKey(recurmax, stmtDepth, canThrow) + "(){",
|
"get " + name + "(){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
defns(),
|
defns(),
|
||||||
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
@@ -1190,23 +1282,24 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
|||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
var prop1 = createObjectKey(recurmax, stmtDepth, canThrow);
|
var prop;
|
||||||
var prop2;
|
|
||||||
do {
|
do {
|
||||||
prop2 = getDotKey();
|
prop = getDotKey();
|
||||||
} while (prop1 == prop2);
|
} while (name == prop);
|
||||||
|
async = false;
|
||||||
s = [
|
s = [
|
||||||
"set " + prop1 + "(" + createVarName(MANDATORY) + "){",
|
"set " + name + "(" + createVarName(MANDATORY) + "){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
defns(),
|
defns(),
|
||||||
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
"this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
|
"this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
|
||||||
"},",
|
"},",
|
||||||
];
|
];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
async = SUPPORT.async && rng(50) == 0;
|
||||||
s = [
|
s = [
|
||||||
createObjectKey(recurmax, stmtDepth, canThrow) + "(" + createParams(NO_DUPLICATE) + "){",
|
(async ? "async " : "") + name + "(" + createParams(save_async, NO_DUPLICATE) + "){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
defns(),
|
defns(),
|
||||||
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||||
@@ -1215,7 +1308,8 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
VAR_NAMES.length = namesLenBefore;
|
async = save_async;
|
||||||
|
VAR_NAMES.length = nameLenBefore;
|
||||||
return filterDirective(s).join("\n");
|
return filterDirective(s).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1406,7 +1500,7 @@ function getVarName(noConst) {
|
|||||||
do {
|
do {
|
||||||
if (--tries < 0) return "a";
|
if (--tries < 0) return "a";
|
||||||
name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
|
name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
|
||||||
} while (!name || avoid_vars.indexOf(name) >= 0 || noConst && block_vars.indexOf(name) >= 0);
|
} while (!name || avoid_vars.indexOf(name) >= 0 || noConst && block_vars.indexOf(name) >= 0 || async && name == "await");
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1418,7 +1512,7 @@ function createVarName(maybe, dontStore) {
|
|||||||
name = VAR_NAMES[rng(VAR_NAMES.length)];
|
name = VAR_NAMES[rng(VAR_NAMES.length)];
|
||||||
if (suffix) name += "_" + suffix;
|
if (suffix) name += "_" + suffix;
|
||||||
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
|
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
|
||||||
if (suffix && !dontStore) VAR_NAMES.push(name);
|
if (!dontStore) VAR_NAMES.push(name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
@@ -1622,7 +1716,7 @@ function patch_try_catch(orig, toplevel) {
|
|||||||
offset: 0,
|
offset: 0,
|
||||||
tries: [],
|
tries: [],
|
||||||
} ];
|
} ];
|
||||||
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)]+)\)|}\s*finally)\s*(?={)/g;
|
var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^)[{]+)\)|}\s*finally)\s*(?={)/g;
|
||||||
while (stack.length) {
|
while (stack.length) {
|
||||||
var code = stack[0].code;
|
var code = stack[0].code;
|
||||||
var offset = stack[0].offset;
|
var offset = stack[0].offset;
|
||||||
|
|||||||
Reference in New Issue
Block a user