Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffcce28ce1 | ||
|
|
9c0feb69e5 | ||
|
|
bc6e105174 | ||
|
|
b91a2459c0 | ||
|
|
b7a57fc69d | ||
|
|
2dbe40b01b | ||
|
|
813ac3ba96 | ||
|
|
220dc95c0d | ||
|
|
8f0521d51d | ||
|
|
f9946767c9 | ||
|
|
58ac5b9bd5 | ||
|
|
66140b459e | ||
|
|
1786c69070 | ||
|
|
95ef4d5377 | ||
|
|
04017215cc | ||
|
|
142bd1bd1a | ||
|
|
8cb509d50e | ||
|
|
baf4903aa7 | ||
|
|
35465d590e | ||
|
|
ccd91b9952 | ||
|
|
47a5e6e17a | ||
|
|
090ee895e1 | ||
|
|
1cd1a1e5ee | ||
|
|
1d835ac17d | ||
|
|
9e07ac4102 |
13
bin/uglifyjs
13
bin/uglifyjs
@@ -342,7 +342,18 @@ function run() {
|
|||||||
}
|
}
|
||||||
fatal(ex);
|
fatal(ex);
|
||||||
} else if (output == "ast") {
|
} else if (output == "ast") {
|
||||||
if (!options.compress && !options.mangle) result.ast.figure_out_scope({});
|
if (!options.compress && !options.mangle) {
|
||||||
|
var toplevel = result.ast;
|
||||||
|
if (!(toplevel instanceof UglifyJS.AST_Toplevel)) {
|
||||||
|
if (!(toplevel instanceof UglifyJS.AST_Statement)) toplevel = new UglifyJS.AST_SimpleStatement({
|
||||||
|
body: toplevel,
|
||||||
|
});
|
||||||
|
toplevel = new UglifyJS.AST_Toplevel({
|
||||||
|
body: [ toplevel ],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
toplevel.figure_out_scope({});
|
||||||
|
}
|
||||||
print(JSON.stringify(result.ast, function(key, value) {
|
print(JSON.stringify(result.ast, function(key, value) {
|
||||||
if (value) switch (key) {
|
if (value) switch (key) {
|
||||||
case "thedef":
|
case "thedef":
|
||||||
|
|||||||
67
lib/ast.js
67
lib/ast.js
@@ -251,9 +251,38 @@ var AST_Block = DEFNODE("Block", "body", {
|
|||||||
},
|
},
|
||||||
}, AST_Statement);
|
}, AST_Statement);
|
||||||
|
|
||||||
|
var AST_BlockScope = DEFNODE("BlockScope", "cname enclosed functions make_def parent_scope variables", {
|
||||||
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
|
$propdoc: {
|
||||||
|
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||||
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||||
|
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||||
|
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
||||||
|
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
||||||
|
},
|
||||||
|
clone: function(deep) {
|
||||||
|
var node = this._clone(deep);
|
||||||
|
if (this.enclosed) node.enclosed = this.enclosed.slice();
|
||||||
|
if (this.functions) node.functions = this.functions.clone();
|
||||||
|
if (this.variables) node.variables = this.variables.clone();
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
pinned: function() {
|
||||||
|
return this.resolve().pinned();
|
||||||
|
},
|
||||||
|
resolve: function() {
|
||||||
|
return this.parent_scope.resolve();
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (this.parent_scope == null) return;
|
||||||
|
if (!(this.parent_scope instanceof AST_BlockScope)) throw new Error("parent_scope must be AST_BlockScope");
|
||||||
|
if (!(this.resolve() instanceof AST_Scope)) throw new Error("must be contained within AST_Scope");
|
||||||
|
},
|
||||||
|
}, AST_Block);
|
||||||
|
|
||||||
var AST_BlockStatement = DEFNODE("BlockStatement", null, {
|
var AST_BlockStatement = DEFNODE("BlockStatement", null, {
|
||||||
$documentation: "A block statement",
|
$documentation: "A block statement",
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
var AST_EmptyStatement = DEFNODE("EmptyStatement", null, {
|
||||||
$documentation: "The empty statement (empty block or simply a semicolon)"
|
$documentation: "The empty statement (empty block or simply a semicolon)"
|
||||||
@@ -412,33 +441,17 @@ var AST_With = DEFNODE("With", "expression", {
|
|||||||
|
|
||||||
/* -----[ scope and functions ]----- */
|
/* -----[ scope and functions ]----- */
|
||||||
|
|
||||||
var AST_Scope = DEFNODE("Scope", "cname enclosed uses_eval uses_with parent_scope functions variables make_def", {
|
var AST_Scope = DEFNODE("Scope", "uses_eval uses_with", {
|
||||||
$documentation: "Base class for all statements introducing a lexical scope",
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
|
||||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
|
||||||
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
||||||
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
||||||
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
|
||||||
functions: "[Object/S] like `variables`, but only lists function declarations",
|
|
||||||
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
|
||||||
},
|
|
||||||
clone: function(deep) {
|
|
||||||
var node = this._clone(deep);
|
|
||||||
if (this.variables) node.variables = this.variables.clone();
|
|
||||||
if (this.functions) node.functions = this.functions.clone();
|
|
||||||
if (this.enclosed) node.enclosed = this.enclosed.slice();
|
|
||||||
return node;
|
|
||||||
},
|
},
|
||||||
pinned: function() {
|
pinned: function() {
|
||||||
return this.uses_eval || this.uses_with;
|
return this.uses_eval || this.uses_with;
|
||||||
},
|
},
|
||||||
_validate: function() {
|
resolve: return_this,
|
||||||
if (this.parent_scope != null) {
|
}, AST_BlockScope);
|
||||||
if (!(this.parent_scope instanceof AST_Scope)) throw new Error("parent_scope must be AST_Scope");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}, AST_Block);
|
|
||||||
|
|
||||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||||
$documentation: "The toplevel scope",
|
$documentation: "The toplevel scope",
|
||||||
@@ -686,28 +699,30 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
|||||||
if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally");
|
if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
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"
|
argname: "[AST_SymbolCatch?] symbol for the exception, or null if not present",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
visitor.visit(node, function() {
|
visitor.visit(node, function() {
|
||||||
node.argname.walk(visitor);
|
if (node.argname) node.argname.walk(visitor);
|
||||||
walk_body(node, visitor);
|
walk_body(node, visitor);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_validate: function() {
|
_validate: function() {
|
||||||
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
if (this.argname != null) {
|
||||||
|
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
var AST_Finally = DEFNODE("Finally", null, {
|
var AST_Finally = DEFNODE("Finally", null, {
|
||||||
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
/* -----[ VAR ]----- */
|
/* -----[ VAR ]----- */
|
||||||
|
|
||||||
|
|||||||
370
lib/compress.js
370
lib/compress.js
@@ -354,6 +354,13 @@ merge(Compressor.prototype, {
|
|||||||
return orig.length == 1 && orig[0] instanceof AST_SymbolFunarg;
|
return orig.length == 1 && orig[0] instanceof AST_SymbolFunarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cross_scope(def, sym) {
|
||||||
|
do {
|
||||||
|
if (def === sym) return false;
|
||||||
|
if (sym instanceof AST_Scope) return true;
|
||||||
|
} while (sym = sym.parent_scope);
|
||||||
|
}
|
||||||
|
|
||||||
(function(def) {
|
(function(def) {
|
||||||
def(AST_Node, noop);
|
def(AST_Node, noop);
|
||||||
|
|
||||||
@@ -368,10 +375,10 @@ merge(Compressor.prototype, {
|
|||||||
&& !(def.init instanceof AST_Function && def.init !== def.scope)
|
&& !(def.init instanceof AST_Function && def.init !== def.scope)
|
||||||
&& def.init;
|
&& def.init;
|
||||||
if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) {
|
if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) {
|
||||||
var scope = ref.scope;
|
var scope = ref.scope.resolve();
|
||||||
do {
|
do {
|
||||||
if (def.scope === scope) return true;
|
if (def.scope === scope) return true;
|
||||||
} while (scope instanceof AST_Function && (scope = scope.parent_scope));
|
} while (scope instanceof AST_Function && (scope = scope.parent_scope.resolve()));
|
||||||
})) {
|
})) {
|
||||||
tw.defun_ids[def.id] = false;
|
tw.defun_ids[def.id] = false;
|
||||||
}
|
}
|
||||||
@@ -506,6 +513,7 @@ merge(Compressor.prototype, {
|
|||||||
function ref_once(compressor, def) {
|
function ref_once(compressor, def) {
|
||||||
return compressor.option("unused")
|
return compressor.option("unused")
|
||||||
&& !def.scope.pinned()
|
&& !def.scope.pinned()
|
||||||
|
&& def.single_use !== false
|
||||||
&& def.references.length - def.recursive_refs == 1;
|
&& def.references.length - def.recursive_refs == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,7 +708,7 @@ merge(Compressor.prototype, {
|
|||||||
tw.in_loop = this;
|
tw.in_loop = this;
|
||||||
push(tw);
|
push(tw);
|
||||||
this.body.walk(tw);
|
this.body.walk(tw);
|
||||||
if (has_break_or_continue(this, tw.parent())) {
|
if (has_loop_control(this, tw.parent())) {
|
||||||
pop(tw);
|
pop(tw);
|
||||||
push(tw);
|
push(tw);
|
||||||
}
|
}
|
||||||
@@ -717,7 +725,7 @@ merge(Compressor.prototype, {
|
|||||||
if (this.condition) this.condition.walk(tw);
|
if (this.condition) this.condition.walk(tw);
|
||||||
this.body.walk(tw);
|
this.body.walk(tw);
|
||||||
if (this.step) {
|
if (this.step) {
|
||||||
if (has_break_or_continue(this, tw.parent())) {
|
if (has_loop_control(this, tw.parent())) {
|
||||||
pop(tw);
|
pop(tw);
|
||||||
push(tw);
|
push(tw);
|
||||||
}
|
}
|
||||||
@@ -840,11 +848,13 @@ merge(Compressor.prototype, {
|
|||||||
&& d.orig[0] instanceof AST_SymbolDefun) {
|
&& d.orig[0] instanceof AST_SymbolDefun) {
|
||||||
tw.loop_ids[d.id] = tw.in_loop;
|
tw.loop_ids[d.id] = tw.in_loop;
|
||||||
}
|
}
|
||||||
var value;
|
if (d.fixed === false) {
|
||||||
if (d.fixed === undefined || !safe_to_read(tw, d)) {
|
var redef = d.redefined();
|
||||||
|
if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
|
||||||
|
} else if (d.fixed === undefined || !safe_to_read(tw, d)) {
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
} else if (d.fixed) {
|
} else if (d.fixed) {
|
||||||
value = this.fixed_value();
|
var value = this.fixed_value();
|
||||||
var recursive = recursive_ref(tw, d);
|
var recursive = recursive_ref(tw, d);
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
d.recursive_refs++;
|
d.recursive_refs++;
|
||||||
@@ -854,7 +864,7 @@ merge(Compressor.prototype, {
|
|||||||
&& !value.pinned()
|
&& !value.pinned()
|
||||||
&& (!d.in_loop || tw.parent() instanceof AST_Call)
|
&& (!d.in_loop || tw.parent() instanceof AST_Call)
|
||||||
|| !d.in_loop
|
|| !d.in_loop
|
||||||
&& d.scope === this.scope
|
&& d.scope === this.scope.resolve()
|
||||||
&& value.is_constant_expression();
|
&& value.is_constant_expression();
|
||||||
} else {
|
} else {
|
||||||
d.single_use = false;
|
d.single_use = false;
|
||||||
@@ -1018,6 +1028,13 @@ merge(Compressor.prototype, {
|
|||||||
return sym instanceof AST_SymbolLambda && def.scope.name === sym;
|
return sym instanceof AST_SymbolLambda && def.scope.name === sym;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function find_scope(compressor) {
|
||||||
|
var level = 0, node;
|
||||||
|
while (node = compressor.parent(level++)) {
|
||||||
|
if (node.variables) return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function is_lhs_read_only(lhs, compressor) {
|
function is_lhs_read_only(lhs, compressor) {
|
||||||
if (lhs instanceof AST_This) return true;
|
if (lhs instanceof AST_This) return true;
|
||||||
if (lhs instanceof AST_SymbolRef) {
|
if (lhs instanceof AST_SymbolRef) {
|
||||||
@@ -1037,18 +1054,6 @@ merge(Compressor.prototype, {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function find_variable(compressor, name) {
|
|
||||||
var scope, i = 0;
|
|
||||||
while (scope = compressor.parent(i++)) {
|
|
||||||
if (scope instanceof AST_Scope) break;
|
|
||||||
if (scope instanceof AST_Catch) {
|
|
||||||
scope = scope.argname.definition().scope;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return scope.find_variable(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
function make_node(ctor, orig, props) {
|
function make_node(ctor, orig, props) {
|
||||||
if (!props) props = {};
|
if (!props) props = {};
|
||||||
if (orig) {
|
if (orig) {
|
||||||
@@ -1192,6 +1197,16 @@ merge(Compressor.prototype, {
|
|||||||
|| node instanceof AST_Undefined;
|
|| node instanceof AST_Undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function declarations_only(node) {
|
||||||
|
return all(node.definitions, function(var_def) {
|
||||||
|
return !var_def.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_declaration(stat) {
|
||||||
|
return stat instanceof AST_Defun || stat instanceof AST_Var && declarations_only(stat);
|
||||||
|
}
|
||||||
|
|
||||||
function tighten_body(statements, compressor) {
|
function tighten_body(statements, compressor) {
|
||||||
var in_loop, in_try, scope;
|
var in_loop, in_try, scope;
|
||||||
find_loop_scope_try();
|
find_loop_scope_try();
|
||||||
@@ -2150,7 +2165,9 @@ merge(Compressor.prototype, {
|
|||||||
function handle_if_return(statements, compressor) {
|
function handle_if_return(statements, compressor) {
|
||||||
var self = compressor.self();
|
var self = compressor.self();
|
||||||
var parent = compressor.parent();
|
var parent = compressor.parent();
|
||||||
var in_lambda = self instanceof AST_Lambda;
|
var in_lambda = last_of(function(node) {
|
||||||
|
return node instanceof AST_Lambda;
|
||||||
|
});
|
||||||
var in_iife = in_lambda && parent && parent.TYPE == "Call";
|
var in_iife = in_lambda && parent && parent.TYPE == "Call";
|
||||||
var multiple_if_returns = has_multiple_if_returns(statements);
|
var multiple_if_returns = has_multiple_if_returns(statements);
|
||||||
for (var i = statements.length; --i >= 0;) {
|
for (var i = statements.length; --i >= 0;) {
|
||||||
@@ -2253,7 +2270,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
//---
|
//---
|
||||||
// if (foo()) return x; return y; => return foo() ? x : y;
|
// if (foo()) return x; return y; => return foo() ? x : y;
|
||||||
if ((in_bool || value) && !stat.alternative && next instanceof AST_Return) {
|
if (!stat.alternative && next instanceof AST_Return) {
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
stat.alternative = next;
|
stat.alternative = next;
|
||||||
@@ -2323,14 +2340,21 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function match_target(target) {
|
function last_of(predicate) {
|
||||||
var block = self, stat, level = 0;
|
var block = self, stat, level = 0;
|
||||||
do {
|
do {
|
||||||
do {
|
do {
|
||||||
if (block === target) return true;
|
if (predicate(block)) return true;
|
||||||
block = compressor.parent(level++);
|
block = compressor.parent(level++);
|
||||||
} while (block instanceof AST_If && (stat = block));
|
} while (block instanceof AST_If && (stat = block));
|
||||||
} while (block instanceof AST_BlockStatement && is_last_statement(block.body, stat));
|
} while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
|
||||||
|
&& is_last_statement(block.body, stat));
|
||||||
|
}
|
||||||
|
|
||||||
|
function match_target(target) {
|
||||||
|
return last_of(function(node) {
|
||||||
|
return node === target;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function can_merge_flow(ab) {
|
function can_merge_flow(ab) {
|
||||||
@@ -2410,16 +2434,6 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function declarations_only(node) {
|
|
||||||
return all(node.definitions, function(var_def) {
|
|
||||||
return !var_def.value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_declaration(stat) {
|
|
||||||
return stat instanceof AST_Defun || stat instanceof AST_Var && declarations_only(stat);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sequencesize(statements, compressor) {
|
function sequencesize(statements, compressor) {
|
||||||
if (statements.length < 2) return;
|
if (statements.length < 2) return;
|
||||||
var seq = [], n = 0;
|
var seq = [], n = 0;
|
||||||
@@ -2588,7 +2602,7 @@ merge(Compressor.prototype, {
|
|||||||
var lhs = expr.left;
|
var lhs = expr.left;
|
||||||
if (!(lhs instanceof AST_SymbolRef)) break;
|
if (!(lhs instanceof AST_SymbolRef)) break;
|
||||||
if (is_undeclared_ref(lhs)) break;
|
if (is_undeclared_ref(lhs)) break;
|
||||||
if (lhs.scope !== scope) break;
|
if (lhs.scope.resolve() !== scope) break;
|
||||||
var def = lhs.definition();
|
var def = lhs.definition();
|
||||||
if (def.scope !== scope) break;
|
if (def.scope !== scope) break;
|
||||||
if (def.orig.length > def.eliminated + 1) break;
|
if (def.orig.length > def.eliminated + 1) break;
|
||||||
@@ -2914,6 +2928,9 @@ merge(Compressor.prototype, {
|
|||||||
this._dot_throw = return_false;
|
this._dot_throw = return_false;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
def(AST_This, function(compressor) {
|
||||||
|
return is_strict(compressor) && !this.scope.new;
|
||||||
|
});
|
||||||
def(AST_UnaryPrefix, function() {
|
def(AST_UnaryPrefix, function() {
|
||||||
return this.operator == "void";
|
return this.operator == "void";
|
||||||
});
|
});
|
||||||
@@ -3947,7 +3964,15 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_Array, function(compressor) {
|
def(AST_Array, function(compressor) {
|
||||||
return any(this.elements, compressor);
|
return any(this.elements, compressor);
|
||||||
});
|
});
|
||||||
def(AST_Assign, return_true);
|
def(AST_Assign, function(compressor) {
|
||||||
|
var lhs = this.left;
|
||||||
|
if (!(lhs instanceof AST_PropAccess)) return true;
|
||||||
|
var node = lhs.expression;
|
||||||
|
return !(node instanceof AST_This)
|
||||||
|
|| !node.scope.new
|
||||||
|
|| lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
|
||||||
|
|| this.right.has_side_effects(compressor);
|
||||||
|
});
|
||||||
def(AST_Binary, function(compressor) {
|
def(AST_Binary, function(compressor) {
|
||||||
return this.left.has_side_effects(compressor)
|
return this.left.has_side_effects(compressor)
|
||||||
|| this.right.has_side_effects(compressor)
|
|| this.right.has_side_effects(compressor)
|
||||||
@@ -4168,13 +4193,7 @@ merge(Compressor.prototype, {
|
|||||||
var scopes = [];
|
var scopes = [];
|
||||||
self.walk(new TreeWalker(function(node, descend) {
|
self.walk(new TreeWalker(function(node, descend) {
|
||||||
if (!result) return true;
|
if (!result) return true;
|
||||||
if (node instanceof AST_Catch) {
|
if (node instanceof AST_BlockScope) {
|
||||||
scopes.push(node.argname.scope);
|
|
||||||
descend();
|
|
||||||
scopes.pop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Scope) {
|
|
||||||
if (node === self) return;
|
if (node === self) return;
|
||||||
scopes.push(node);
|
scopes.push(node);
|
||||||
descend();
|
descend();
|
||||||
@@ -4182,14 +4201,14 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
if (self.inlined) {
|
if (self.inlined || node.redef) {
|
||||||
result = false;
|
result = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (self.variables.has(node.name)) return true;
|
if (self.variables.has(node.name)) return true;
|
||||||
var def = node.definition();
|
var def = node.definition();
|
||||||
if (member(def.scope, scopes)) return true;
|
if (member(def.scope, scopes)) return true;
|
||||||
if (scope) {
|
if (scope && !def.redefined()) {
|
||||||
var scope_def = scope.find_variable(node);
|
var scope_def = scope.find_variable(node);
|
||||||
if (def.undeclared ? !scope_def : scope_def === def) {
|
if (def.undeclared ? !scope_def : scope_def === def) {
|
||||||
result = "f";
|
result = "f";
|
||||||
@@ -4266,13 +4285,17 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function trim_block(stat) {
|
||||||
|
switch (stat.body.length) {
|
||||||
|
case 1: return stat.body[0];
|
||||||
|
case 0: return make_node(AST_EmptyStatement, stat);
|
||||||
|
}
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
|
|
||||||
OPT(AST_BlockStatement, function(self, compressor) {
|
OPT(AST_BlockStatement, function(self, compressor) {
|
||||||
self.body = tighten_body(self.body, compressor);
|
self.body = tighten_body(self.body, compressor);
|
||||||
switch (self.body.length) {
|
return trim_block(self);
|
||||||
case 1: return self.body[0];
|
|
||||||
case 0: return make_node(AST_EmptyStatement, self);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Function, function(self, compressor) {
|
OPT(AST_Function, function(self, compressor) {
|
||||||
@@ -4415,7 +4438,12 @@ merge(Compressor.prototype, {
|
|||||||
push();
|
push();
|
||||||
segment.block = node;
|
segment.block = node;
|
||||||
if (node === self) root = segment;
|
if (node === self) root = segment;
|
||||||
if (node instanceof AST_Lambda && node.name) references[node.name.definition().id] = false;
|
if (node instanceof AST_Lambda) {
|
||||||
|
if (node.name) references[node.name.definition().id] = false;
|
||||||
|
if (node.uses_arguments && !tw.has_directive("use strict")) node.argnames.forEach(function(node) {
|
||||||
|
references[node.definition().id] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
descend();
|
descend();
|
||||||
pop();
|
pop();
|
||||||
return true;
|
return true;
|
||||||
@@ -4448,15 +4476,18 @@ merge(Compressor.prototype, {
|
|||||||
push();
|
push();
|
||||||
segment.block = node;
|
segment.block = node;
|
||||||
walk_body(node, tw);
|
walk_body(node, tw);
|
||||||
if (node.bcatch) {
|
|
||||||
var def = node.bcatch.argname.definition();
|
|
||||||
references[def.id] = false;
|
|
||||||
if (def = def.redefined()) references[def.id] = false;
|
|
||||||
pop();
|
|
||||||
push();
|
|
||||||
walk_body(node.bcatch, tw);
|
|
||||||
}
|
|
||||||
pop();
|
pop();
|
||||||
|
if (node.bcatch) {
|
||||||
|
if (node.bcatch.argname) {
|
||||||
|
var def = node.bcatch.argname.definition();
|
||||||
|
references[def.id] = false;
|
||||||
|
if (def = def.redefined()) references[def.id] = false;
|
||||||
|
}
|
||||||
|
push();
|
||||||
|
if (node.bfinally) segment.block = node.bcatch;
|
||||||
|
walk_body(node.bcatch, tw);
|
||||||
|
pop();
|
||||||
|
}
|
||||||
if (node.bfinally) node.bfinally.walk(tw);
|
if (node.bfinally) node.bfinally.walk(tw);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -4482,6 +4513,7 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
tw.directives = Object.create(compressor.directives);
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
var merged = Object.create(null);
|
var merged = Object.create(null);
|
||||||
while (first.length && last.length) {
|
while (first.length && last.length) {
|
||||||
@@ -4792,15 +4824,15 @@ 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];
|
||||||
var def = sym.definition();
|
var def = sym.definition();
|
||||||
if (!(def.id in in_use_ids)) {
|
if (def.id in in_use_ids) {
|
||||||
|
trim = false;
|
||||||
|
if (indexOf_assign(def, sym) < 0) sym.__unused = null;
|
||||||
|
} else {
|
||||||
sym.__unused = true;
|
sym.__unused = true;
|
||||||
if (trim) {
|
if (trim) {
|
||||||
log(sym, "Dropping unused function argument {name}");
|
log(sym, "Dropping unused function argument {name}");
|
||||||
a.pop();
|
a.pop();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
trim = false;
|
|
||||||
if (indexOf_assign(def, sym) < 0) sym.__unused = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fns_with_marked_args.push(node);
|
fns_with_marked_args.push(node);
|
||||||
@@ -4853,8 +4885,8 @@ merge(Compressor.prototype, {
|
|||||||
&& (old_def.name == def.name.name || all(old_def.references, function(ref) {
|
&& (old_def.name == def.name.name || all(old_def.references, function(ref) {
|
||||||
return ref.scope.find_variable(def.name) === def.name.definition();
|
return ref.scope.find_variable(def.name) === def.name.definition();
|
||||||
})))
|
})))
|
||||||
&& can_rename(def.value, def.name.name)
|
&& can_declare_defun()
|
||||||
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
|
&& can_rename(def.value, def.name.name)) {
|
||||||
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
||||||
var defun = make_node(AST_Defun, def, def.value);
|
var defun = make_node(AST_Defun, def, def.value);
|
||||||
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
|
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
|
||||||
@@ -4910,6 +4942,13 @@ merge(Compressor.prototype, {
|
|||||||
var def = fn.variables.get(name);
|
var def = fn.variables.get(name);
|
||||||
return !def || fn.name && def === fn.name.definition();
|
return !def || fn.name && def === fn.name.definition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function can_declare_defun() {
|
||||||
|
if (compressor.has_directive("use strict")) return parent instanceof AST_Scope;
|
||||||
|
return parent instanceof AST_Block
|
||||||
|
|| parent instanceof AST_For && parent.init === node
|
||||||
|
|| parent instanceof AST_If;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
switch (head.length) {
|
switch (head.length) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -5164,6 +5203,13 @@ merge(Compressor.prototype, {
|
|||||||
in_use_ids[node_def.id] = true;
|
in_use_ids[node_def.id] = true;
|
||||||
in_use.push(node_def);
|
in_use.push(node_def);
|
||||||
}
|
}
|
||||||
|
if (cross_scope(node_def.scope, node.scope)) {
|
||||||
|
var redef = node_def.redefined();
|
||||||
|
if (redef && !(redef.id in in_use_ids)) {
|
||||||
|
in_use_ids[redef.id] = true;
|
||||||
|
in_use.push(redef);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (track_assigns(node_def, node)) add_assigns(node_def, node);
|
if (track_assigns(node_def, node)) add_assigns(node_def, node);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -5391,7 +5437,7 @@ merge(Compressor.prototype, {
|
|||||||
process_boolean_returns(this, compressor);
|
process_boolean_returns(this, compressor);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("var_names", function() {
|
AST_BlockScope.DEFMETHOD("var_names", function() {
|
||||||
var var_names = this._var_names;
|
var var_names = this._var_names;
|
||||||
if (!var_names) {
|
if (!var_names) {
|
||||||
this._var_names = var_names = Object.create(null);
|
this._var_names = var_names = Object.create(null);
|
||||||
@@ -5615,36 +5661,53 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
def(AST_Call, function(compressor, first_in_statement) {
|
def(AST_Call, function(compressor, first_in_statement) {
|
||||||
if (!this.is_expr_pure(compressor)) {
|
var self = this;
|
||||||
var exp = this.expression;
|
if (self.is_expr_pure(compressor)) {
|
||||||
if (this.is_call_pure(compressor)) {
|
if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
|
||||||
var exprs = this.args.slice();
|
var args = trim(self.args, compressor, first_in_statement);
|
||||||
exprs.unshift(exp.expression);
|
return args && make_sequence(self, args);
|
||||||
exprs = trim(exprs, compressor, first_in_statement);
|
|
||||||
return exprs && make_sequence(this, exprs);
|
|
||||||
}
|
|
||||||
if (exp instanceof AST_Function) {
|
|
||||||
if (exp.name) {
|
|
||||||
var def = exp.name.definition();
|
|
||||||
if (def.references.length > def.replaced) return this;
|
|
||||||
}
|
|
||||||
exp.process_expression(false, function(node) {
|
|
||||||
var value = node.value && node.value.drop_side_effect_free(compressor, true);
|
|
||||||
return value ? make_node(AST_SimpleStatement, node, {
|
|
||||||
body: value
|
|
||||||
}) : make_node(AST_EmptyStatement, node);
|
|
||||||
});
|
|
||||||
scan_local_returns(exp, function(node) {
|
|
||||||
if (node.value) node.value = node.value.drop_side_effect_free(compressor);
|
|
||||||
});
|
|
||||||
// always shallow clone to ensure stripping of negated IIFEs
|
|
||||||
return this.clone();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
if (this.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
|
var exp = self.expression;
|
||||||
var args = trim(this.args, compressor, first_in_statement);
|
if (self.is_call_pure(compressor)) {
|
||||||
return args && make_sequence(this, args);
|
var exprs = self.args.slice();
|
||||||
|
exprs.unshift(exp.expression);
|
||||||
|
exprs = trim(exprs, compressor, first_in_statement);
|
||||||
|
return exprs && make_sequence(self, exprs);
|
||||||
|
}
|
||||||
|
var def;
|
||||||
|
if (exp instanceof AST_Function
|
||||||
|
&& !(exp.name && (def = exp.name.definition()).references.length > def.replaced)) {
|
||||||
|
exp.process_expression(false, function(node) {
|
||||||
|
var value = node.value && node.value.drop_side_effect_free(compressor, true);
|
||||||
|
return value ? make_node(AST_SimpleStatement, node, {
|
||||||
|
body: value
|
||||||
|
}) : make_node(AST_EmptyStatement, node);
|
||||||
|
});
|
||||||
|
scan_local_returns(exp, function(node) {
|
||||||
|
if (node.value) node.value = node.value.drop_side_effect_free(compressor);
|
||||||
|
});
|
||||||
|
// always shallow clone to ensure stripping of negated IIFEs
|
||||||
|
self = self.clone();
|
||||||
|
}
|
||||||
|
if (self instanceof AST_New) {
|
||||||
|
var fn = exp;
|
||||||
|
if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
|
||||||
|
if (fn instanceof AST_Lambda) {
|
||||||
|
fn.new = true;
|
||||||
|
var assign_this_only = all(fn.body, function(stat) {
|
||||||
|
return !stat.has_side_effects(compressor);
|
||||||
|
});
|
||||||
|
delete fn.new;
|
||||||
|
if (assign_this_only) {
|
||||||
|
var exprs = self.args.slice();
|
||||||
|
exprs.unshift(exp);
|
||||||
|
exprs = trim(exprs, compressor, first_in_statement);
|
||||||
|
return exprs && make_sequence(self, exprs);
|
||||||
|
}
|
||||||
|
if (!fn.contains_this()) return make_node(AST_Call, self, self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
});
|
});
|
||||||
def(AST_Conditional, function(compressor) {
|
def(AST_Conditional, function(compressor) {
|
||||||
var consequent = this.consequent.drop_side_effect_free(compressor);
|
var consequent = this.consequent.drop_side_effect_free(compressor);
|
||||||
@@ -5766,11 +5829,12 @@ merge(Compressor.prototype, {
|
|||||||
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
|
return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
|
||||||
});
|
});
|
||||||
|
|
||||||
function has_break_or_continue(loop, parent) {
|
function has_loop_control(loop, parent, type) {
|
||||||
|
if (!type) type = AST_LoopControl;
|
||||||
var found = false;
|
var found = false;
|
||||||
var tw = new TreeWalker(function(node) {
|
var tw = new TreeWalker(function(node) {
|
||||||
if (found || node instanceof AST_Scope) return true;
|
if (found || node instanceof AST_Scope) return true;
|
||||||
if (node instanceof AST_LoopControl && tw.loopcontrol_target(node) === loop) {
|
if (node instanceof type && tw.loopcontrol_target(node) === loop) {
|
||||||
return found = true;
|
return found = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -5794,7 +5858,7 @@ merge(Compressor.prototype, {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
if (!has_break_or_continue(self, compressor.parent())) {
|
if (!has_loop_control(self, compressor.parent())) {
|
||||||
return make_node(AST_BlockStatement, self.body, {
|
return make_node(AST_BlockStatement, self.body, {
|
||||||
body: [
|
body: [
|
||||||
self.body,
|
self.body,
|
||||||
@@ -5805,6 +5869,33 @@ merge(Compressor.prototype, {
|
|||||||
}).optimize(compressor);
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
|
||||||
|
var body = self.body.body;
|
||||||
|
for (var i = body.length; --i >= 0;) {
|
||||||
|
var stat = body[i];
|
||||||
|
if (stat instanceof AST_If
|
||||||
|
&& !stat.alternative
|
||||||
|
&& stat.body instanceof AST_Break
|
||||||
|
&& compressor.loopcontrol_target(stat.body) === self) {
|
||||||
|
self.condition = make_node(AST_Binary, self, {
|
||||||
|
operator: "&&",
|
||||||
|
left: stat.condition.negate(compressor),
|
||||||
|
right: self.condition,
|
||||||
|
});
|
||||||
|
body.splice(i, 1);
|
||||||
|
} else if (stat instanceof AST_SimpleStatement) {
|
||||||
|
self.condition = make_sequence(self, [
|
||||||
|
stat.body,
|
||||||
|
self.condition,
|
||||||
|
]);
|
||||||
|
body.splice(i, 1);
|
||||||
|
} else if (!is_declaration(stat)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.body = trim_block(self.body);
|
||||||
|
}
|
||||||
|
if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
|
||||||
if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
|
if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
|
||||||
condition: make_sequence(self.condition, [
|
condition: make_sequence(self.condition, [
|
||||||
self.body.body,
|
self.body.body,
|
||||||
@@ -6736,7 +6827,7 @@ merge(Compressor.prototype, {
|
|||||||
if (self.args.length == 0) return make_node(AST_Function, self, {
|
if (self.args.length == 0) return make_node(AST_Function, self, {
|
||||||
argnames: [],
|
argnames: [],
|
||||||
body: []
|
body: []
|
||||||
}).init_scope_vars(exp.scope);
|
}).init_vars(exp.scope);
|
||||||
if (all(self.args, function(x) {
|
if (all(self.args, function(x) {
|
||||||
return x instanceof AST_String;
|
return x instanceof AST_String;
|
||||||
})) {
|
})) {
|
||||||
@@ -6804,7 +6895,7 @@ merge(Compressor.prototype, {
|
|||||||
&& !fn.pinned()
|
&& !fn.pinned()
|
||||||
&& !(fn.name && fn instanceof AST_Function)
|
&& !(fn.name && fn instanceof AST_Function)
|
||||||
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
|
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
|
||||||
&& fn.is_constant_expression(compressor.find_parent(AST_Scope)))
|
&& fn.is_constant_expression(find_scope(compressor)))
|
||||||
&& (value = can_flatten_body(stat))
|
&& (value = can_flatten_body(stat))
|
||||||
&& !fn.contains_this()) {
|
&& !fn.contains_this()) {
|
||||||
var replacing = exp === fn || compressor.option("unused") && def.references.length - def.replaced == 1;
|
var replacing = exp === fn || compressor.option("unused") && def.references.length - def.replaced == 1;
|
||||||
@@ -6830,7 +6921,7 @@ merge(Compressor.prototype, {
|
|||||||
return arg;
|
return arg;
|
||||||
})).optimize(compressor);
|
})).optimize(compressor);
|
||||||
node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
||||||
if (replacing || best_of(compressor, self, node) === node) {
|
if (replacing || best_of_expression(node, self) === node) {
|
||||||
refs.forEach(function(ref) {
|
refs.forEach(function(ref) {
|
||||||
var def = ref.definition();
|
var def = ref.definition();
|
||||||
def.references.push(ref);
|
def.references.push(ref);
|
||||||
@@ -6991,6 +7082,9 @@ merge(Compressor.prototype, {
|
|||||||
var stat = fn.body[i];
|
var stat = fn.body[i];
|
||||||
if (stat instanceof AST_Defun) {
|
if (stat instanceof AST_Defun) {
|
||||||
if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
|
if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
|
||||||
|
if (!all(stat.enclosed, function(def) {
|
||||||
|
return def.scope === stat || !catches[def.name];
|
||||||
|
})) return false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(stat instanceof AST_Var)) continue;
|
if (!(stat instanceof AST_Var)) continue;
|
||||||
@@ -7012,7 +7106,7 @@ merge(Compressor.prototype, {
|
|||||||
child = scope;
|
child = scope;
|
||||||
scope = compressor.parent(++level);
|
scope = compressor.parent(++level);
|
||||||
if (scope instanceof AST_Catch) {
|
if (scope instanceof AST_Catch) {
|
||||||
catches[scope.argname.name] = true;
|
if (scope.argname) catches[scope.argname.name] = true;
|
||||||
} else if (scope instanceof AST_DWLoop) {
|
} else if (scope instanceof AST_DWLoop) {
|
||||||
in_loop = [];
|
in_loop = [];
|
||||||
} else if (scope instanceof AST_For) {
|
} else if (scope instanceof AST_For) {
|
||||||
@@ -7027,7 +7121,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
} while (!(scope instanceof AST_Scope));
|
} while (!(scope instanceof AST_Scope));
|
||||||
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
|
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
|
||||||
&& (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope));
|
&& (exp !== fn || fn.parent_scope.resolve() === compressor.find_parent(AST_Scope));
|
||||||
var inline = compressor.option("inline");
|
var inline = compressor.option("inline");
|
||||||
var used = Object.create(catches);
|
var used = Object.create(catches);
|
||||||
if (!can_inject_args(catches, used, inline >= 2 && safe_to_inject)) return false;
|
if (!can_inject_args(catches, used, inline >= 2 && safe_to_inject)) return false;
|
||||||
@@ -7046,9 +7140,10 @@ merge(Compressor.prototype, {
|
|||||||
value: null
|
value: null
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
if (!value) return;
|
||||||
var sym = make_node(AST_SymbolRef, name, name);
|
var sym = make_node(AST_SymbolRef, name, name);
|
||||||
def.references.push(sym);
|
def.references.push(sym);
|
||||||
if (value) expressions.push(make_node(AST_Assign, self, {
|
expressions.push(make_node(AST_Assign, self, {
|
||||||
operator: "=",
|
operator: "=",
|
||||||
left: sym,
|
left: sym,
|
||||||
right: value
|
right: value
|
||||||
@@ -7069,7 +7164,12 @@ merge(Compressor.prototype, {
|
|||||||
var symbol = make_node(AST_SymbolVar, name, name);
|
var symbol = make_node(AST_SymbolVar, name, name);
|
||||||
name.definition().orig.push(symbol);
|
name.definition().orig.push(symbol);
|
||||||
if (!value && in_loop) value = make_node(AST_Undefined, self);
|
if (!value && in_loop) value = make_node(AST_Undefined, self);
|
||||||
append_var(decls, expressions, symbol, value);
|
if ("__unused" in name) {
|
||||||
|
append_var(decls, expressions, symbol);
|
||||||
|
if (value) expressions.push(value);
|
||||||
|
} else {
|
||||||
|
append_var(decls, expressions, symbol, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
decls.reverse();
|
decls.reverse();
|
||||||
@@ -7381,6 +7481,15 @@ merge(Compressor.prototype, {
|
|||||||
|| node instanceof AST_Object;
|
|| node instanceof AST_Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function repeatable(compressor, node) {
|
||||||
|
if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
|
||||||
|
if (node instanceof AST_Sub) {
|
||||||
|
return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Symbol) return true;
|
||||||
|
return !node.has_side_effects(compressor);
|
||||||
|
}
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor) {
|
OPT(AST_Binary, function(self, compressor) {
|
||||||
function reversible() {
|
function reversible() {
|
||||||
return self.left.is_constant()
|
return self.left.is_constant()
|
||||||
@@ -7449,7 +7558,7 @@ merge(Compressor.prototype, {
|
|||||||
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
|
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
|
||||||
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
|
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
|
||||||
(self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
|
(self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
|
||||||
can_self_compare(self.left) && self.left.equivalent_to(self.right)) {
|
repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
|
||||||
self.operator = self.operator.slice(0, 2);
|
self.operator = self.operator.slice(0, 2);
|
||||||
}
|
}
|
||||||
// XXX: intentionally falling down to the next case
|
// XXX: intentionally falling down to the next case
|
||||||
@@ -7954,13 +8063,6 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return try_evaluate(compressor, self);
|
return try_evaluate(compressor, self);
|
||||||
|
|
||||||
function can_self_compare(node) {
|
|
||||||
if (node instanceof AST_Dot) return can_self_compare(node.expression);
|
|
||||||
if (node instanceof AST_Sub) return can_self_compare(node.expression) && can_self_compare(node.property);
|
|
||||||
if (node instanceof AST_Symbol) return true;
|
|
||||||
return !node.has_side_effects(compressor);
|
|
||||||
}
|
|
||||||
|
|
||||||
function align(ref, op) {
|
function align(ref, op) {
|
||||||
switch (ref) {
|
switch (ref) {
|
||||||
case "-":
|
case "-":
|
||||||
@@ -8041,7 +8143,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!compressor.option("ie8")
|
if (!compressor.option("ie8")
|
||||||
&& is_undeclared_ref(self)
|
&& is_undeclared_ref(self)
|
||||||
// testing against `self.scope.uses_with` is an optimization
|
// testing against `self.scope.uses_with` is an optimization
|
||||||
&& !(self.scope.uses_with && compressor.find_parent(AST_With))) {
|
&& !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
|
||||||
switch (self.name) {
|
switch (self.name) {
|
||||||
case "undefined":
|
case "undefined":
|
||||||
return make_node(AST_Undefined, self).optimize(compressor);
|
return make_node(AST_Undefined, self).optimize(compressor);
|
||||||
@@ -8058,14 +8160,14 @@ merge(Compressor.prototype, {
|
|||||||
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
|
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
|
||||||
if (single_use) {
|
if (single_use) {
|
||||||
if (fixed instanceof AST_Lambda) {
|
if (fixed instanceof AST_Lambda) {
|
||||||
if ((def.scope !== self.scope || def.in_loop)
|
if ((def.scope !== self.scope.resolve() || def.in_loop)
|
||||||
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
|
&& (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
|
||||||
single_use = false;
|
single_use = false;
|
||||||
} else if (recursive_ref(compressor, def)) {
|
} else if (recursive_ref(compressor, def)) {
|
||||||
single_use = false;
|
single_use = false;
|
||||||
} else if (fixed.name && fixed.name.definition() !== def) {
|
} else if (fixed.name && fixed.name.definition() !== def) {
|
||||||
single_use = false;
|
single_use = false;
|
||||||
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
|
} else if (fixed.parent_scope !== self.scope.resolve() || def.orig[0] instanceof AST_SymbolFunarg) {
|
||||||
single_use = fixed.is_constant_expression(self.scope);
|
single_use = fixed.is_constant_expression(self.scope);
|
||||||
if (single_use == "f") {
|
if (single_use == "f") {
|
||||||
var scope = self.scope;
|
var scope = self.scope;
|
||||||
@@ -8213,7 +8315,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_Undefined, function(self, compressor) {
|
OPT(AST_Undefined, function(self, compressor) {
|
||||||
if (compressor.option("unsafe_undefined")) {
|
if (compressor.option("unsafe_undefined")) {
|
||||||
var undef = find_variable(compressor, "undefined");
|
var undef = find_scope(compressor).find_variable("undefined");
|
||||||
if (undef) {
|
if (undef) {
|
||||||
var ref = make_node(AST_SymbolRef, self, {
|
var ref = make_node(AST_SymbolRef, self, {
|
||||||
name : "undefined",
|
name : "undefined",
|
||||||
@@ -8239,7 +8341,7 @@ merge(Compressor.prototype, {
|
|||||||
if (lhs && is_atomic(lhs, self)) return self;
|
if (lhs && is_atomic(lhs, self)) return self;
|
||||||
if (compressor.option("keep_infinity")
|
if (compressor.option("keep_infinity")
|
||||||
&& !(lhs && !is_atomic(lhs, self))
|
&& !(lhs && !is_atomic(lhs, self))
|
||||||
&& !find_variable(compressor, "Infinity"))
|
&& !find_scope(compressor).find_variable("Infinity"))
|
||||||
return self;
|
return self;
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "/",
|
operator: "/",
|
||||||
@@ -8254,8 +8356,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_NaN, function(self, compressor) {
|
OPT(AST_NaN, function(self, compressor) {
|
||||||
var lhs = is_lhs(compressor.self(), compressor.parent());
|
var lhs = is_lhs(compressor.self(), compressor.parent());
|
||||||
if (lhs && !is_atomic(lhs, self)
|
if (lhs && !is_atomic(lhs, self) || find_scope(compressor).find_variable("NaN")) {
|
||||||
|| find_variable(compressor, "NaN")) {
|
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: "/",
|
operator: "/",
|
||||||
left: make_node(AST_Number, self, {
|
left: make_node(AST_Number, self, {
|
||||||
@@ -8422,14 +8523,13 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Conditional, function(self, compressor) {
|
OPT(AST_Conditional, function(self, compressor) {
|
||||||
if (!compressor.option("conditionals")) return self;
|
if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
|
||||||
// This looks like lift_sequences(), should probably be under "sequences"
|
|
||||||
if (self.condition instanceof AST_Sequence) {
|
|
||||||
var expressions = self.condition.expressions.slice();
|
var expressions = self.condition.expressions.slice();
|
||||||
self.condition = expressions.pop();
|
self.condition = expressions.pop();
|
||||||
expressions.push(self);
|
expressions.push(self);
|
||||||
return make_sequence(self, expressions);
|
return make_sequence(self, expressions);
|
||||||
}
|
}
|
||||||
|
if (!compressor.option("conditionals")) return self;
|
||||||
var condition = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
|
var condition = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
|
||||||
if (!condition) {
|
if (!condition) {
|
||||||
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
|
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||||
@@ -8450,15 +8550,19 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var consequent = self.consequent;
|
var consequent = self.consequent;
|
||||||
var alternative = self.alternative;
|
var alternative = self.alternative;
|
||||||
// x ? x : y => x || y
|
if (repeatable(compressor, condition)) {
|
||||||
if (condition instanceof AST_SymbolRef
|
// x ? x : y => x || y
|
||||||
&& consequent instanceof AST_SymbolRef
|
if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
|
||||||
&& condition.definition() === consequent.definition()) {
|
|
||||||
return make_node(AST_Binary, self, {
|
|
||||||
operator: "||",
|
operator: "||",
|
||||||
left: condition,
|
left: condition,
|
||||||
right: alternative
|
right: alternative,
|
||||||
});
|
}).optimize(compressor);
|
||||||
|
// x ? y : x => x && y
|
||||||
|
if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
|
||||||
|
operator: "&&",
|
||||||
|
left: condition,
|
||||||
|
right: consequent,
|
||||||
|
}).optimize(compressor);
|
||||||
}
|
}
|
||||||
// if (foo) exp = something; else exp = something_else;
|
// if (foo) exp = something; else exp = something_else;
|
||||||
// |
|
// |
|
||||||
@@ -8846,7 +8950,7 @@ merge(Compressor.prototype, {
|
|||||||
&& expr instanceof AST_SymbolRef
|
&& expr instanceof AST_SymbolRef
|
||||||
&& is_arguments(def = expr.definition())
|
&& is_arguments(def = expr.definition())
|
||||||
&& prop instanceof AST_Number
|
&& prop instanceof AST_Number
|
||||||
&& (fn = expr.scope) === find_lambda()) {
|
&& (fn = expr.scope.resolve()) === find_lambda()) {
|
||||||
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 = [];
|
||||||
@@ -9020,7 +9124,7 @@ merge(Compressor.prototype, {
|
|||||||
self.expression = make_node(AST_Function, self.expression, {
|
self.expression = make_node(AST_Function, self.expression, {
|
||||||
argnames: [],
|
argnames: [],
|
||||||
body: []
|
body: []
|
||||||
}).init_scope_vars(exp.scope);
|
}).init_vars(exp.scope);
|
||||||
break;
|
break;
|
||||||
case "Number":
|
case "Number":
|
||||||
self.expression = make_node(AST_Number, self.expression, {
|
self.expression = make_node(AST_Number, self.expression, {
|
||||||
|
|||||||
@@ -1099,10 +1099,12 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Catch, function(output) {
|
DEFPRINT(AST_Catch, function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
output.print("catch");
|
output.print("catch");
|
||||||
output.space();
|
if (self.argname) {
|
||||||
output.with_parens(function() {
|
output.space();
|
||||||
self.argname.print(output);
|
output.with_parens(function() {
|
||||||
});
|
self.argname.print(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output);
|
print_braced(self, output);
|
||||||
});
|
});
|
||||||
|
|||||||
40
lib/parse.js
40
lib/parse.js
@@ -60,8 +60,9 @@ KEYWORDS_ATOM = makePredicate(KEYWORDS_ATOM);
|
|||||||
|
|
||||||
var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
|
var OPERATOR_CHARS = makePredicate(characters("+-*&%=<>!?|~^"));
|
||||||
|
|
||||||
var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
|
var RE_BIN_NUMBER = /^0b([01]+)$/i;
|
||||||
var RE_OCT_NUMBER = /^0[0-7]+$/;
|
var RE_HEX_NUMBER = /^0x([0-9a-f]+)$/i;
|
||||||
|
var RE_OCT_NUMBER = /^0o?([0-7]+)$/i;
|
||||||
|
|
||||||
var OPERATORS = makePredicate([
|
var OPERATORS = makePredicate([
|
||||||
"in",
|
"in",
|
||||||
@@ -147,10 +148,6 @@ function is_digit(code) {
|
|||||||
return code >= 48 && code <= 57;
|
return code >= 48 && code <= 57;
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_alphanumeric_char(code) {
|
|
||||||
return is_digit(code) || is_letter(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_unicode_digit(code) {
|
function is_unicode_digit(code) {
|
||||||
return UNICODE.digit.test(String.fromCharCode(code));
|
return UNICODE.digit.test(String.fromCharCode(code));
|
||||||
}
|
}
|
||||||
@@ -184,14 +181,12 @@ function is_identifier_string(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parse_js_number(num) {
|
function parse_js_number(num) {
|
||||||
if (RE_HEX_NUMBER.test(num)) {
|
var match;
|
||||||
return parseInt(num.substr(2), 16);
|
if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
|
||||||
} else if (RE_OCT_NUMBER.test(num)) {
|
if (match = RE_HEX_NUMBER.exec(num)) return parseInt(match[1], 16);
|
||||||
return parseInt(num.substr(1), 8);
|
if (match = RE_OCT_NUMBER.exec(num)) return parseInt(match[1], 8);
|
||||||
} else {
|
var val = parseFloat(num);
|
||||||
var val = parseFloat(num);
|
if (val == num) return val;
|
||||||
if (val == num) return val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function JS_Parse_Error(message, filename, line, col, pos) {
|
function JS_Parse_Error(message, filename, line, col, pos) {
|
||||||
@@ -347,11 +342,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
case (after_e = false, 46): // .
|
case (after_e = false, 46): // .
|
||||||
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
|
return (!has_dot && !has_x && !has_e) ? (has_dot = true) : false;
|
||||||
}
|
}
|
||||||
return is_alphanumeric_char(code);
|
return is_digit(code) || is_letter(code) || ch == "_";
|
||||||
});
|
});
|
||||||
if (prefix) num = prefix + num;
|
if (prefix) num = prefix + num;
|
||||||
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
|
if (/^0[0-7_]+$/.test(num)) {
|
||||||
parse_error("Legacy octal literals are not allowed in strict mode");
|
if (next_token.has_directive("use strict")) parse_error("Legacy octal literals are not allowed in strict mode");
|
||||||
|
} else {
|
||||||
|
num = num.replace(has_x ? /([1-9a-f]|.0)_(?=[0-9a-f])/gi : /([1-9]|.0)_(?=[0-9])/gi, "$1");
|
||||||
}
|
}
|
||||||
var valid = parse_js_number(num);
|
var valid = parse_js_number(num);
|
||||||
if (!isNaN(valid)) return token("num", valid);
|
if (!isNaN(valid)) return token("num", valid);
|
||||||
@@ -1133,9 +1130,12 @@ function parse($TEXT, options) {
|
|||||||
if (is("keyword", "catch")) {
|
if (is("keyword", "catch")) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
next();
|
next();
|
||||||
expect("(");
|
var name = null;
|
||||||
var name = as_symbol(AST_SymbolCatch);
|
if (is("punc", "(")) {
|
||||||
expect(")");
|
next();
|
||||||
|
name = as_symbol(AST_SymbolCatch);
|
||||||
|
expect(")");
|
||||||
|
}
|
||||||
bcatch = new AST_Catch({
|
bcatch = new AST_Catch({
|
||||||
start : start,
|
start : start,
|
||||||
argname : name,
|
argname : name,
|
||||||
|
|||||||
159
lib/scope.js
159
lib/scope.js
@@ -59,13 +59,9 @@ function SymbolDef(id, scope, orig, init) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SymbolDef.prototype = {
|
SymbolDef.prototype = {
|
||||||
unmangleable: function(options) {
|
forEach: function(fn) {
|
||||||
return this.global && !options.toplevel
|
this.orig.forEach(fn);
|
||||||
|| this.undeclared
|
this.references.forEach(fn);
|
||||||
|| !options.eval && this.scope.pinned()
|
|
||||||
|| options.keep_fnames
|
|
||||||
&& (this.orig[0] instanceof AST_SymbolLambda
|
|
||||||
|| this.orig[0] instanceof AST_SymbolDefun);
|
|
||||||
},
|
},
|
||||||
mangle: function(options) {
|
mangle: function(options) {
|
||||||
var cache = options.cache && options.cache.props;
|
var cache = options.cache && options.cache.props;
|
||||||
@@ -85,7 +81,15 @@ SymbolDef.prototype = {
|
|||||||
},
|
},
|
||||||
redefined: function() {
|
redefined: function() {
|
||||||
return this.defun && this.defun.variables.get(this.name);
|
return this.defun && this.defun.variables.get(this.name);
|
||||||
}
|
},
|
||||||
|
unmangleable: function(options) {
|
||||||
|
return this.global && !options.toplevel
|
||||||
|
|| this.undeclared
|
||||||
|
|| !options.eval && this.scope.pinned()
|
||||||
|
|| options.keep_fnames
|
||||||
|
&& (this.orig[0] instanceof AST_SymbolLambda
|
||||||
|
|| this.orig[0] instanceof AST_SymbolDefun);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||||
@@ -100,26 +104,35 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
var next_def_id = 0;
|
var next_def_id = 0;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
var tw = new TreeWalker(function(node, descend) {
|
var tw = new TreeWalker(function(node, descend) {
|
||||||
if (node instanceof AST_Catch) {
|
if (node instanceof AST_Defun) {
|
||||||
var save_scope = scope;
|
node.name.walk(tw);
|
||||||
scope = new AST_Scope(node);
|
walk_scope(function() {
|
||||||
scope.init_scope_vars(save_scope);
|
node.argnames.forEach(function(argname) {
|
||||||
descend();
|
argname.walk(tw);
|
||||||
scope = save_scope;
|
});
|
||||||
|
walk_body(node, tw);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Try) {
|
||||||
node.init_scope_vars(scope);
|
walk_scope(function() {
|
||||||
var save_scope = scope;
|
walk_body(node, tw);
|
||||||
var save_defun = defun;
|
});
|
||||||
defun = scope = node;
|
if (node.bcatch) node.bcatch.walk(tw);
|
||||||
descend();
|
if (node.bfinally) node.bfinally.walk(tw);
|
||||||
scope = save_scope;
|
return true;
|
||||||
defun = save_defun;
|
}
|
||||||
|
if (node instanceof AST_BlockScope) {
|
||||||
|
walk_scope(descend);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_With) {
|
if (node instanceof AST_With) {
|
||||||
for (var s = scope; s; s = s.parent_scope) s.uses_with = true;
|
var s = scope;
|
||||||
|
do {
|
||||||
|
s = s.resolve();
|
||||||
|
if (s.uses_with) break;
|
||||||
|
s.uses_with = true;
|
||||||
|
} while (s = s.parent_scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
@@ -129,25 +142,41 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
node.thedef = node;
|
node.thedef = node;
|
||||||
node.references = [];
|
node.references = [];
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolDefun) {
|
if (node instanceof AST_SymbolCatch) {
|
||||||
// This should be defined in the parent scope, as we encounter the
|
scope.def_variable(node).defun = defun;
|
||||||
// AST_Defun node before getting to its AST_Symbol.
|
} else if (node instanceof AST_SymbolDefun) {
|
||||||
(node.scope = defun.parent_scope.resolve()).def_function(node, defun);
|
defun.def_function(node, tw.parent());
|
||||||
|
entangle(defun, scope);
|
||||||
|
} else if (node instanceof AST_SymbolFunarg) {
|
||||||
|
defun.def_variable(node);
|
||||||
|
entangle(defun, scope);
|
||||||
} else if (node instanceof AST_SymbolLambda) {
|
} else if (node instanceof AST_SymbolLambda) {
|
||||||
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
|
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
|
||||||
if (options.ie8) def.defun = defun.parent_scope.resolve();
|
if (options.ie8) def.defun = defun.parent_scope.resolve();
|
||||||
} else if (node instanceof AST_SymbolVar) {
|
} else if (node instanceof AST_SymbolVar) {
|
||||||
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
|
defun.def_variable(node, null);
|
||||||
if (defun !== scope) {
|
entangle(defun, scope);
|
||||||
node.mark_enclosed(options);
|
}
|
||||||
var def = scope.find_variable(node);
|
|
||||||
if (node.thedef !== def) {
|
function walk_scope(descend) {
|
||||||
node.thedef = def;
|
node.init_vars(scope);
|
||||||
}
|
var save_defun = defun;
|
||||||
node.reference(options);
|
var save_scope = scope;
|
||||||
}
|
if (node instanceof AST_Scope) defun = node;
|
||||||
} else if (node instanceof AST_SymbolCatch) {
|
scope = node;
|
||||||
scope.def_variable(node).defun = defun;
|
descend();
|
||||||
|
scope = save_scope;
|
||||||
|
defun = save_defun;
|
||||||
|
}
|
||||||
|
|
||||||
|
function entangle(defun, scope) {
|
||||||
|
if (defun === scope) return;
|
||||||
|
node.mark_enclosed(options);
|
||||||
|
var def = scope.find_variable(node);
|
||||||
|
if (node.thedef === def) return;
|
||||||
|
node.thedef = def;
|
||||||
|
def.orig.push(node);
|
||||||
|
node.mark_enclosed(options);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.make_def = function(orig, init) {
|
self.make_def = function(orig, init) {
|
||||||
@@ -167,15 +196,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
var sym = node.scope.find_variable(name);
|
var sym = node.scope.find_variable(name);
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
sym = self.def_global(node);
|
sym = self.def_global(node);
|
||||||
} else if (sym.scope instanceof AST_Lambda && name == "arguments") {
|
} else if (name == "arguments" && sym.scope instanceof AST_Lambda) {
|
||||||
sym.scope.uses_arguments = true;
|
sym.scope.uses_arguments = true;
|
||||||
}
|
}
|
||||||
if (name == "eval") {
|
if (name == "eval") {
|
||||||
var parent = tw.parent();
|
var parent = tw.parent();
|
||||||
if (parent.TYPE == "Call" && parent.expression === node) {
|
if (parent.TYPE == "Call" && parent.expression === node) {
|
||||||
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
|
var s = node.scope;
|
||||||
|
do {
|
||||||
|
s = s.resolve();
|
||||||
|
if (s.uses_eval) break;
|
||||||
s.uses_eval = true;
|
s.uses_eval = true;
|
||||||
}
|
} while (s = s.parent_scope);
|
||||||
} else if (sym.undeclared) {
|
} else if (sym.undeclared) {
|
||||||
self.uses_eval = true;
|
self.uses_eval = true;
|
||||||
}
|
}
|
||||||
@@ -234,7 +266,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
new_def = scope.def_variable(node);
|
new_def = scope.def_variable(node);
|
||||||
}
|
}
|
||||||
old_def.defun = new_def.scope;
|
old_def.defun = new_def.scope;
|
||||||
old_def.orig.concat(old_def.references).forEach(function(node) {
|
old_def.forEach(function(node) {
|
||||||
|
node.redef = true;
|
||||||
node.thedef = new_def;
|
node.thedef = new_def;
|
||||||
node.reference(options);
|
node.reference(options);
|
||||||
});
|
});
|
||||||
@@ -256,22 +289,28 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function init_scope_vars(scope, parent) {
|
function init_block_vars(scope, parent) {
|
||||||
scope.cname = -1; // the current index for mangling functions/variables
|
scope.cname = -1; // the current index for mangling functions/variables
|
||||||
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
|
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
|
||||||
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
|
||||||
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
|
||||||
scope.parent_scope = parent; // the parent scope (null if this is the top level)
|
scope.parent_scope = parent; // the parent scope (null if this is the top level)
|
||||||
scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||||
scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||||
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
|
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
function init_scope_vars(scope, parent) {
|
||||||
|
init_block_vars(scope, parent);
|
||||||
|
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
||||||
|
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||||
|
}
|
||||||
|
|
||||||
|
AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
|
||||||
|
init_block_vars(this, parent_scope);
|
||||||
|
});
|
||||||
|
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
|
||||||
init_scope_vars(this, parent_scope);
|
init_scope_vars(this, parent_scope);
|
||||||
});
|
});
|
||||||
|
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
|
||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
|
||||||
init_scope_vars(this, parent_scope);
|
init_scope_vars(this, parent_scope);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
this.def_variable(new AST_SymbolFunarg({
|
this.def_variable(new AST_SymbolFunarg({
|
||||||
@@ -300,20 +339,20 @@ AST_Symbol.DEFMETHOD("reference", function(options) {
|
|||||||
this.mark_enclosed(options);
|
this.mark_enclosed(options);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("find_variable", function(name) {
|
AST_BlockScope.DEFMETHOD("find_variable", function(name) {
|
||||||
if (name instanceof AST_Symbol) name = name.name;
|
if (name instanceof AST_Symbol) name = name.name;
|
||||||
return this.variables.get(name)
|
return this.variables.get(name)
|
||||||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_function", function(symbol, init) {
|
AST_BlockScope.DEFMETHOD("def_function", function(symbol, init) {
|
||||||
var def = this.def_variable(symbol, init);
|
var def = this.def_variable(symbol, init);
|
||||||
if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
||||||
this.functions.set(symbol.name, def);
|
this.functions.set(symbol.name, def);
|
||||||
return def;
|
return def;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
AST_BlockScope.DEFMETHOD("def_variable", function(symbol, init) {
|
||||||
var def = this.variables.get(symbol.name);
|
var def = this.variables.get(symbol.name);
|
||||||
if (def) {
|
if (def) {
|
||||||
def.orig.push(symbol);
|
def.orig.push(symbol);
|
||||||
@@ -326,12 +365,6 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
|||||||
return symbol.thedef = def;
|
return symbol.thedef = def;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("resolve", return_this);
|
|
||||||
AST_Scope.DEFMETHOD("resolve", function() {
|
|
||||||
return this.parent_scope.resolve();
|
|
||||||
});
|
|
||||||
AST_Toplevel.DEFMETHOD("resolve", return_this);
|
|
||||||
|
|
||||||
function names_in_use(scope, options) {
|
function names_in_use(scope, options) {
|
||||||
var names = scope.names_in_use;
|
var names = scope.names_in_use;
|
||||||
if (!names) {
|
if (!names) {
|
||||||
@@ -353,7 +386,7 @@ function next_mangled_name(scope, options, def) {
|
|||||||
var holes = scope.cname_holes;
|
var holes = scope.cname_holes;
|
||||||
var names = Object.create(null);
|
var names = Object.create(null);
|
||||||
var scopes = [ scope ];
|
var scopes = [ scope ];
|
||||||
def.references.forEach(function(sym) {
|
def.forEach(function(sym) {
|
||||||
var scope = sym.scope;
|
var scope = sym.scope;
|
||||||
do {
|
do {
|
||||||
if (scopes.indexOf(scope) < 0) {
|
if (scopes.indexOf(scope) < 0) {
|
||||||
@@ -457,7 +490,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
|||||||
node.mangled_name = name;
|
node.mangled_name = name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!options.ie8 && node instanceof AST_Catch) {
|
if (!options.ie8 && node instanceof AST_Catch && node.argname) {
|
||||||
var def = node.argname.definition();
|
var def = node.argname.definition();
|
||||||
var redef = defer_redef(def, node.argname);
|
var redef = defer_redef(def, node.argname);
|
||||||
descend();
|
descend();
|
||||||
@@ -495,8 +528,7 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
|
|||||||
options.reserved.forEach(to_avoid);
|
options.reserved.forEach(to_avoid);
|
||||||
this.globals.each(add_def);
|
this.globals.each(add_def);
|
||||||
this.walk(new TreeWalker(function(node) {
|
this.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Scope) node.variables.each(add_def);
|
if (node instanceof AST_BlockScope) node.variables.each(add_def);
|
||||||
if (node instanceof AST_SymbolCatch) add_def(node.definition());
|
|
||||||
}));
|
}));
|
||||||
return avoid;
|
return avoid;
|
||||||
|
|
||||||
@@ -520,8 +552,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
|||||||
var cname = 0;
|
var cname = 0;
|
||||||
this.globals.each(rename);
|
this.globals.each(rename);
|
||||||
this.walk(new TreeWalker(function(node) {
|
this.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_Scope) node.variables.each(rename);
|
if (node instanceof AST_BlockScope) node.variables.each(rename);
|
||||||
if (node instanceof AST_SymbolCatch) rename(node.definition());
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function next_name() {
|
function next_name() {
|
||||||
@@ -539,7 +570,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
|||||||
var redef = def.redefined();
|
var redef = def.redefined();
|
||||||
var name = redef ? redef.rename || redef.name : next_name();
|
var name = redef ? redef.rename || redef.name : next_name();
|
||||||
def.rename = name;
|
def.rename = name;
|
||||||
def.orig.concat(def.references).forEach(function(sym) {
|
def.forEach(function(sym) {
|
||||||
if (sym.definition() === def) sym.name = name;
|
if (sym.definition() === def) sym.name = name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
|
if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
|
||||||
});
|
});
|
||||||
DEF(AST_Catch, function(self, tw) {
|
DEF(AST_Catch, function(self, tw) {
|
||||||
self.argname = self.argname.transform(tw);
|
if (self.argname) self.argname = self.argname.transform(tw);
|
||||||
self.body = do_list(self.body, tw);
|
self.body = do_list(self.body, tw);
|
||||||
});
|
});
|
||||||
DEF(AST_Definitions, function(self, tw) {
|
DEF(AST_Definitions, function(self, tw) {
|
||||||
|
|||||||
@@ -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.11.0",
|
"version": "3.11.2",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1159,7 +1159,7 @@ issue_1645_2: {
|
|||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
condition_symbol_matches_consequent: {
|
condition_matches_consequent: {
|
||||||
options = {
|
options = {
|
||||||
conditionals: true,
|
conditionals: true,
|
||||||
}
|
}
|
||||||
@@ -1188,6 +1188,35 @@ condition_symbol_matches_consequent: {
|
|||||||
expect_stdout: "3 7 true 4"
|
expect_stdout: "3 7 true 4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
condition_matches_alternative: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function foo(x, y) {
|
||||||
|
return x.p ? y[0] : x.p;
|
||||||
|
}
|
||||||
|
function bar() {
|
||||||
|
return g ? h : g;
|
||||||
|
}
|
||||||
|
var g = 4;
|
||||||
|
var h = 5;
|
||||||
|
console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo(x, y) {
|
||||||
|
return x.p && y[0];
|
||||||
|
}
|
||||||
|
function bar() {
|
||||||
|
return g && h;
|
||||||
|
}
|
||||||
|
var g = 4;
|
||||||
|
var h = 5;
|
||||||
|
console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar());
|
||||||
|
}
|
||||||
|
expect_stdout: "null 0 false 5"
|
||||||
|
}
|
||||||
|
|
||||||
delete_conditional_1: {
|
delete_conditional_1: {
|
||||||
options = {
|
options = {
|
||||||
booleans: true,
|
booleans: true,
|
||||||
|
|||||||
@@ -2992,3 +2992,96 @@ issue_4146: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "function"
|
expect_stdout: "function"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var_catch_redefined: {
|
||||||
|
options = {
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "FAIL";
|
||||||
|
try {
|
||||||
|
throw "PASS";
|
||||||
|
} catch (a) {
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "FAIL";
|
||||||
|
try {
|
||||||
|
throw "PASS";
|
||||||
|
} catch (a) {
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_catch_redefined: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4184: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
var a = function() {}, b = [ a, 1 && b, a = {} ];
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch (a) {
|
||||||
|
{
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
var b = [ function() {}, 1 && b, {} ];
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch (a) {
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
}
|
||||||
|
|||||||
@@ -4808,3 +4808,271 @@ issue_4155: {
|
|||||||
"function",
|
"function",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4159: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 42, c = function(b) {
|
||||||
|
(b = a) && console.log(a++, b);
|
||||||
|
}(c = a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 42;
|
||||||
|
(b = a) && console.log(a++, b);
|
||||||
|
var b;
|
||||||
|
}
|
||||||
|
expect_stdout: "42 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_inline: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a, b) {
|
||||||
|
function g(c) {
|
||||||
|
return c >> 1;
|
||||||
|
}
|
||||||
|
return g(a) + g(b);
|
||||||
|
}
|
||||||
|
console.log(f(13, 31));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a, b) {
|
||||||
|
return (a >> 1) + (b >> 1);
|
||||||
|
}
|
||||||
|
console.log(f(13, 31));
|
||||||
|
}
|
||||||
|
expect_stdout: "21"
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_inline_catch_redefined: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
console.log(a, a, g());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4171_1: {
|
||||||
|
options = {
|
||||||
|
functions: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
try {
|
||||||
|
while (a)
|
||||||
|
var e = function() {};
|
||||||
|
} catch (e) {
|
||||||
|
return function() {
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}(!console));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
try {
|
||||||
|
while (a)
|
||||||
|
var e = function() {};
|
||||||
|
} catch (e) {
|
||||||
|
return function() {
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}(!console));
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4171_2: {
|
||||||
|
options = {
|
||||||
|
functions: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
try {
|
||||||
|
while (a);
|
||||||
|
} catch (e) {
|
||||||
|
return function() {
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
var e = function() {};
|
||||||
|
}
|
||||||
|
}(!console));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
try {
|
||||||
|
while (a);
|
||||||
|
} catch (e) {
|
||||||
|
return function() {
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
function e() {}
|
||||||
|
}
|
||||||
|
}(!console));
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
catch_defun: {
|
||||||
|
mangle = {
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch (a) {
|
||||||
|
function f() {
|
||||||
|
return typeof a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(f());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch (o) {
|
||||||
|
function t() {
|
||||||
|
return typeof o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(t());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
catch_no_argname: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "PASS";
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw a;
|
||||||
|
} catch {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "PASS";
|
||||||
|
try {
|
||||||
|
throw a;
|
||||||
|
} catch {
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
console.log(a, a, a);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS PASS PASS",
|
||||||
|
"PASS PASS PASS",
|
||||||
|
]
|
||||||
|
node_version: ">=10"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4186: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_funcs: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
return function() {
|
||||||
|
function f() {
|
||||||
|
if (1)
|
||||||
|
g();
|
||||||
|
else
|
||||||
|
(function() {
|
||||||
|
return f;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
function g() {
|
||||||
|
if (1) {
|
||||||
|
if (0)
|
||||||
|
h;
|
||||||
|
else
|
||||||
|
h();
|
||||||
|
var key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function h() {
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}()());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
return function f() {
|
||||||
|
1 ? void (1 && (0 ? h : h(), 0)) : function() {
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
function h() {
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "function"
|
||||||
|
}
|
||||||
|
|||||||
@@ -2714,3 +2714,148 @@ issue_2737: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "function"
|
expect_stdout: "function"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single_use_catch_redefined: {
|
||||||
|
options = {
|
||||||
|
ie8: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
single_use_inline_catch_redefined: {
|
||||||
|
options = {
|
||||||
|
ie8: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(g());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_inline_catch_redefined: {
|
||||||
|
options = {
|
||||||
|
ie8: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
function f() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw 2;
|
||||||
|
} catch (a) {
|
||||||
|
function g() {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
console.log(a, f(), g());
|
||||||
|
}
|
||||||
|
console.log(a, a, g());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4186: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
evaluate: true,
|
||||||
|
ie8: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
mangle = {
|
||||||
|
ie8: true,
|
||||||
|
toplevel: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
(function NaN() {
|
||||||
|
var a = 1;
|
||||||
|
while (a--)
|
||||||
|
try {} finally {
|
||||||
|
console.log(0/0);
|
||||||
|
var b;
|
||||||
|
}
|
||||||
|
})(f);
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
NaN;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
(function NaN() {
|
||||||
|
var n = 1;
|
||||||
|
while (n--)
|
||||||
|
console.log(0/0);
|
||||||
|
})();
|
||||||
|
})();
|
||||||
|
NaN;
|
||||||
|
}
|
||||||
|
expect_stdout: "NaN"
|
||||||
|
}
|
||||||
|
|||||||
@@ -663,3 +663,88 @@ nested_if_continue: {
|
|||||||
"even 21",
|
"even 21",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nested_if_return: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
if_return: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
if (A) {
|
||||||
|
if (B)
|
||||||
|
return B;
|
||||||
|
if (C)
|
||||||
|
return D;
|
||||||
|
if (E)
|
||||||
|
return F;
|
||||||
|
if (G)
|
||||||
|
return H;
|
||||||
|
if (I) {
|
||||||
|
if (J)
|
||||||
|
return K;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (L) {
|
||||||
|
if (M)
|
||||||
|
return;
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
if (A)
|
||||||
|
return B || (C ? D : E ? F : G ? H : I ? J ? K : void 0 : L && !M ? N : void 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_866_1: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
if_return: true,
|
||||||
|
sequences: false,
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
if (a)
|
||||||
|
return "";
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
if (a)
|
||||||
|
return "";
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_866_2: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
if_return: true,
|
||||||
|
sequences: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
if (a)
|
||||||
|
if (b)
|
||||||
|
c;
|
||||||
|
else
|
||||||
|
return d;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
if (a) {
|
||||||
|
if (!b)
|
||||||
|
return d;
|
||||||
|
c;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ multiple_functions: {
|
|||||||
( function() {
|
( function() {
|
||||||
// NOTE: other compression steps will reduce this
|
// NOTE: other compression steps will reduce this
|
||||||
// down to just `window`.
|
// down to just `window`.
|
||||||
if ( window );
|
if ( !window );
|
||||||
function f() {}
|
function f() {}
|
||||||
function g() {}
|
function g() {}
|
||||||
} )();
|
} )();
|
||||||
@@ -38,7 +38,7 @@ single_function: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
( function() {
|
( function() {
|
||||||
if ( window );
|
if ( !window );
|
||||||
function f() {}
|
function f() {}
|
||||||
} )();
|
} )();
|
||||||
}
|
}
|
||||||
@@ -67,7 +67,7 @@ deeply_nested: {
|
|||||||
// NOTE: other compression steps will reduce this
|
// NOTE: other compression steps will reduce this
|
||||||
// down to just `window`.
|
// down to just `window`.
|
||||||
if ( window )
|
if ( window )
|
||||||
if (document);
|
if ( !document );
|
||||||
function f() {}
|
function f() {}
|
||||||
function g() {}
|
function g() {}
|
||||||
function h() {}
|
function h() {}
|
||||||
|
|||||||
@@ -151,15 +151,18 @@ Infinity_not_in_with_scope: {
|
|||||||
unused: true,
|
unused: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = { Infinity: 'oInfinity' };
|
var o = { Infinity: "FAIL" };
|
||||||
var vInfinity = "Infinity";
|
var vInfinity = "Infinity";
|
||||||
vInfinity = Infinity;
|
vInfinity = Infinity;
|
||||||
|
console.log(vInfinity);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
var o = { Infinity: 'oInfinity' }
|
var o = { Infinity: "FAIL" };
|
||||||
var vInfinity = "Infinity"
|
var vInfinity = "Infinity";
|
||||||
vInfinity = 1/0
|
vInfinity = 1/0;
|
||||||
|
console.log(vInfinity);
|
||||||
}
|
}
|
||||||
|
expect_stdout: "Infinity"
|
||||||
}
|
}
|
||||||
|
|
||||||
Infinity_in_with_scope: {
|
Infinity_in_with_scope: {
|
||||||
@@ -167,15 +170,18 @@ Infinity_in_with_scope: {
|
|||||||
unused: true,
|
unused: true,
|
||||||
}
|
}
|
||||||
input: {
|
input: {
|
||||||
var o = { Infinity: 'oInfinity' };
|
var o = { Infinity: "PASS" };
|
||||||
var vInfinity = "Infinity";
|
var vInfinity = "Infinity";
|
||||||
with (o) { vInfinity = Infinity; }
|
with (o) { vInfinity = Infinity; }
|
||||||
|
console.log(vInfinity);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
var o = { Infinity: 'oInfinity' }
|
var o = { Infinity: "PASS" };
|
||||||
var vInfinity = "Infinity"
|
var vInfinity = "Infinity";
|
||||||
with (o) vInfinity = Infinity
|
with (o) vInfinity = Infinity;
|
||||||
|
console.log(vInfinity);
|
||||||
}
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
assorted_Infinity_NaN_undefined_in_with_scope: {
|
assorted_Infinity_NaN_undefined_in_with_scope: {
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ evaluate: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
issue_1532: {
|
issue_1532_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
loops: true,
|
loops: true,
|
||||||
@@ -210,18 +210,56 @@ issue_1532: {
|
|||||||
function f(x, y) {
|
function f(x, y) {
|
||||||
do {
|
do {
|
||||||
if (x) break;
|
if (x) break;
|
||||||
foo();
|
console.log(y);
|
||||||
} while (false);
|
} while (false);
|
||||||
}
|
}
|
||||||
|
f(null, "PASS");
|
||||||
|
f(42, "FAIL");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
for (; !x && (console.log(y), false););
|
||||||
|
}
|
||||||
|
f(null, "PASS");
|
||||||
|
f(42, "FAIL");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1532_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
do {
|
||||||
|
if (x) {
|
||||||
|
console.log(x);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
console.log(y);
|
||||||
|
} while (false);
|
||||||
|
}
|
||||||
|
f(null, "PASS");
|
||||||
|
f(42, "FAIL");
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
function f(x, y) {
|
function f(x, y) {
|
||||||
do {
|
do {
|
||||||
if (x) break;
|
if (x) {
|
||||||
foo();
|
console.log(x);
|
||||||
} while (false);
|
break;
|
||||||
|
}
|
||||||
|
} while (console.log(y), false);
|
||||||
}
|
}
|
||||||
|
f(null, "PASS");
|
||||||
|
f(42, "FAIL");
|
||||||
}
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"PASS",
|
||||||
|
"42",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
issue_186: {
|
issue_186: {
|
||||||
@@ -1088,3 +1126,74 @@ issue_4091_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "undefined"
|
expect_stdout: "undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4182_1: {
|
||||||
|
options = {
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
console.log("FAIL");
|
||||||
|
} while (0);
|
||||||
|
console.log("PASS");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
console.log("FAIL");
|
||||||
|
} while (0);
|
||||||
|
console.log("PASS");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4182_2: {
|
||||||
|
options = {
|
||||||
|
loops: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
L: do {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
continue L;
|
||||||
|
}
|
||||||
|
console.log("FAIL");
|
||||||
|
} while (0);
|
||||||
|
console.log("FAIL");
|
||||||
|
} while (0);
|
||||||
|
console.log("PASS");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
L: do {
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
return;
|
||||||
|
} finally {
|
||||||
|
continue L;
|
||||||
|
}
|
||||||
|
} while (console.log("FAIL"), 0);
|
||||||
|
console.log("FAIL");
|
||||||
|
} while (0);
|
||||||
|
console.log("PASS");
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ issue_4112: {
|
|||||||
var o = e;
|
var o = e;
|
||||||
for (e in o);
|
for (e in o);
|
||||||
var a = function() {};
|
var a = function() {};
|
||||||
console.log;
|
console.log(typeof a);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
@@ -479,12 +479,15 @@ issue_4112: {
|
|||||||
var a = e;
|
var a = e;
|
||||||
for (e in a);
|
for (e in a);
|
||||||
a = function() {};
|
a = function() {};
|
||||||
console.log;
|
console.log(typeof a);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
expect_stdout: "function"
|
expect_stdout: [
|
||||||
|
"function",
|
||||||
|
"function",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
issue_4115: {
|
issue_4115: {
|
||||||
@@ -2617,9 +2620,9 @@ issue_4126_1: {
|
|||||||
try {
|
try {
|
||||||
console.log("PASS");
|
console.log("PASS");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var c = a;
|
var b = a;
|
||||||
} finally {
|
} finally {
|
||||||
var c = c;
|
var c = b;
|
||||||
}
|
}
|
||||||
console.log(c);
|
console.log(c);
|
||||||
}
|
}
|
||||||
@@ -2860,3 +2863,151 @@ issue_4155: {
|
|||||||
"function",
|
"function",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4157_1: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
loops: true,
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
for (var a = "FAIL"; a; a++)
|
||||||
|
return;
|
||||||
|
var b = 0;
|
||||||
|
} finally {
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
var a = "FAIL";
|
||||||
|
if (a)
|
||||||
|
return;
|
||||||
|
var b = 0;
|
||||||
|
} finally {
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4157_2: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
loops: true,
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
for (var a = e; a; a++)
|
||||||
|
return;
|
||||||
|
var b = 0;
|
||||||
|
} finally {
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
var a = e;
|
||||||
|
if (a)
|
||||||
|
return;
|
||||||
|
var b = 0;
|
||||||
|
} finally {
|
||||||
|
console.log(b);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4168: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = {
|
||||||
|
f: function(a, b, c) {
|
||||||
|
var d = a.d;
|
||||||
|
var e = b.e;
|
||||||
|
var f = c.f;
|
||||||
|
this.g(arguments);
|
||||||
|
if (d)
|
||||||
|
console.log(e, f);
|
||||||
|
},
|
||||||
|
g: function(args) {
|
||||||
|
console.log(args[0], args[1], args[2]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
o.f("PASS", true, 42);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = {
|
||||||
|
f: function(a, b, c) {
|
||||||
|
var d = a.d;
|
||||||
|
var e = b.e;
|
||||||
|
var f = c.f;
|
||||||
|
this.g(arguments);
|
||||||
|
if (d)
|
||||||
|
console.log(e, f);
|
||||||
|
},
|
||||||
|
g: function(args) {
|
||||||
|
console.log(args[0], args[1], args[2]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
o.f("PASS", true, 42);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS true 42"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4168_use_strict: {
|
||||||
|
options = {
|
||||||
|
merge_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
var o = {
|
||||||
|
f: function(a, b, c) {
|
||||||
|
var d = a.d;
|
||||||
|
var e = b.e;
|
||||||
|
var f = c.f;
|
||||||
|
this.g(arguments);
|
||||||
|
if (d)
|
||||||
|
console.log(e, f);
|
||||||
|
},
|
||||||
|
g: function(args) {
|
||||||
|
console.log(args[0], args[1], args[2]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
o.f("PASS", true, 42);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
var o = {
|
||||||
|
f: function(d, e, f) {
|
||||||
|
var d = d.d;
|
||||||
|
var e = e.e;
|
||||||
|
var f = f.f;
|
||||||
|
this.g(arguments);
|
||||||
|
if (d)
|
||||||
|
console.log(e, f);
|
||||||
|
},
|
||||||
|
g: function(args) {
|
||||||
|
console.log(args[0], args[1], args[2]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
o.f("PASS", true, 42);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS true 42"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1123,11 +1123,7 @@ new_this: {
|
|||||||
}
|
}
|
||||||
}.f(42);
|
}.f(42);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {}
|
||||||
new function(a) {
|
|
||||||
this.a = a;
|
|
||||||
}(42);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
issue_2513: {
|
issue_2513: {
|
||||||
|
|||||||
@@ -5374,11 +5374,11 @@ defun_catch_4: {
|
|||||||
try {
|
try {
|
||||||
throw 42;
|
throw 42;
|
||||||
} catch (a) {
|
} catch (a) {
|
||||||
|
function a() {}
|
||||||
console.log(a);
|
console.log(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect_stdout: "42"
|
expect_stdout: true
|
||||||
node_version: "<=4"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defun_catch_5: {
|
defun_catch_5: {
|
||||||
@@ -5400,10 +5400,10 @@ defun_catch_5: {
|
|||||||
throw 42;
|
throw 42;
|
||||||
} catch (a) {
|
} catch (a) {
|
||||||
console.log(a);
|
console.log(a);
|
||||||
|
function a() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect_stdout: "42"
|
expect_stdout: true
|
||||||
node_version: "<=4"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defun_catch_6: {
|
defun_catch_6: {
|
||||||
@@ -7535,3 +7535,69 @@ global_assign: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4188_1: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
while (A)
|
||||||
|
var a = function() {}, b = a;
|
||||||
|
} catch (a) {
|
||||||
|
console.log(function() {
|
||||||
|
return typeof a;
|
||||||
|
}(), typeof b);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
while (A)
|
||||||
|
var a = function() {}, b = a;
|
||||||
|
} catch (a) {
|
||||||
|
console.log(function() {
|
||||||
|
return typeof a;
|
||||||
|
}(), typeof b);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "object undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4188_2: {
|
||||||
|
options = {
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch (a) {
|
||||||
|
console.log(function() {
|
||||||
|
return typeof a;
|
||||||
|
}(), typeof b);
|
||||||
|
}
|
||||||
|
while (!console)
|
||||||
|
var a = function() {}, b = a;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
throw 42;
|
||||||
|
} catch (a) {
|
||||||
|
console.log(function() {
|
||||||
|
return typeof a;
|
||||||
|
}(), typeof b);
|
||||||
|
}
|
||||||
|
while (!console)
|
||||||
|
var a = function() {}, b = a;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "number undefined"
|
||||||
|
}
|
||||||
|
|||||||
@@ -416,3 +416,20 @@ issue_4008: {
|
|||||||
"PASS",
|
"PASS",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trim_new: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
new function(a) {
|
||||||
|
console.log(a);
|
||||||
|
}("PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(a) {
|
||||||
|
console.log(a);
|
||||||
|
})("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
new function(){console.log(3)};
|
console.log(3);
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSJ9
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUEwQkEsUUFBUUMsSUFBSSJ9
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
new function(){console.log(3)};
|
console.log(3);
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSJ9
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUEwQkEsUUFBUUMsSUFBSSJ9
|
||||||
|
|||||||
@@ -28,4 +28,65 @@ describe("Number literals", function() {
|
|||||||
assert.throws(test(inputs[i]), error, inputs[i]);
|
assert.throws(test(inputs[i]), error, inputs[i]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
it("Should parse binary, hexadecimal, octal and underscore correctly", function() {
|
||||||
|
[
|
||||||
|
"42",
|
||||||
|
"4_2",
|
||||||
|
"052",
|
||||||
|
"0o52",
|
||||||
|
"0O52",
|
||||||
|
"0o5_2",
|
||||||
|
"0x2a",
|
||||||
|
"0X2A",
|
||||||
|
"0x2_a",
|
||||||
|
"0b101010",
|
||||||
|
"0B101010",
|
||||||
|
"0b101_010",
|
||||||
|
"0.0000000042e+10",
|
||||||
|
"0.0000000042E+10",
|
||||||
|
"0.0_000000042e+10",
|
||||||
|
"0.0000000042e+1_0",
|
||||||
|
"0.000_000_004_2e+1_0",
|
||||||
|
"0.000_000_004_2e+1_0-0B101_010+0x2_A-0o5_2+4_2",
|
||||||
|
].forEach(function(code) {
|
||||||
|
var result = UglifyJS.minify(code, {
|
||||||
|
compress: {
|
||||||
|
expression: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
assert.strictEqual(result.code, "42;");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should reject invalid use of underscore", function() {
|
||||||
|
[
|
||||||
|
"_42",
|
||||||
|
"_+42",
|
||||||
|
"+_42",
|
||||||
|
].forEach(function(code) {
|
||||||
|
var node = UglifyJS.parse(code, {
|
||||||
|
expression: true,
|
||||||
|
});
|
||||||
|
assert.ok(!node.is_constant(), code);
|
||||||
|
assert.ok(!(node instanceof UglifyJS.AST_Statement), code);
|
||||||
|
});
|
||||||
|
[
|
||||||
|
"42_",
|
||||||
|
"4__2",
|
||||||
|
"0_52",
|
||||||
|
"05_2",
|
||||||
|
"0_o52",
|
||||||
|
"0o_52",
|
||||||
|
"0.0000000042_e10",
|
||||||
|
"0.0000000042e_10",
|
||||||
|
"0.0000000042e_+10",
|
||||||
|
"0.0000000042e+_10",
|
||||||
|
].forEach(function(code) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(code);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error;
|
||||||
|
}, code);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ describe("With", function() {
|
|||||||
var ast = UglifyJS.parse("with(e) {f(1, 2)}");
|
var ast = UglifyJS.parse("with(e) {f(1, 2)}");
|
||||||
ast.figure_out_scope();
|
ast.figure_out_scope();
|
||||||
assert.equal(ast.uses_with, true);
|
assert.equal(ast.uses_with, true);
|
||||||
assert.equal(ast.body[0].expression.scope.uses_with, true);
|
assert.equal(ast.body[0].expression.scope.resolve().uses_with, true);
|
||||||
assert.equal(ast.body[0].body.body[0].body.expression.scope.uses_with, true);
|
assert.equal(ast.body[0].body.body[0].body.expression.scope.resolve().uses_with, true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -427,7 +427,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) {
|
if (node instanceof U.AST_Catch && node.argname) {
|
||||||
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)),
|
||||||
@@ -499,7 +499,26 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
|
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
testcase = try_beautify(testcase, minify_options, differs.unminified_result, result_cache, max_timeout);
|
var beautified = U.minify(testcase, {
|
||||||
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
|
output: {
|
||||||
|
beautify: true,
|
||||||
|
braces: true,
|
||||||
|
comments: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
testcase = {
|
||||||
|
code: testcase,
|
||||||
|
};
|
||||||
|
if (!beautified.error) {
|
||||||
|
diff = test_for_diff(beautified.code, minify_options, result_cache, max_timeout);
|
||||||
|
if (diff && !diff.timed_out && !diff.error) {
|
||||||
|
testcase = beautified;
|
||||||
|
testcase.code = "// (beautified)\n" + testcase.code;
|
||||||
|
differs = diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
var lines = [ "" ];
|
var lines = [ "" ];
|
||||||
if (isNaN(max_timeout)) {
|
if (isNaN(max_timeout)) {
|
||||||
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack)));
|
lines.push("// minify error: " + to_comment(strip_color_codes(differs.minified_result.stack)));
|
||||||
@@ -538,34 +557,6 @@ function trim_trailing_whitespace(value) {
|
|||||||
return ("" + value).replace(/\s+$/, "");
|
return ("" + value).replace(/\s+$/, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function try_beautify(testcase, minify_options, expected, result_cache, timeout) {
|
|
||||||
var result = U.minify(testcase, {
|
|
||||||
compress: false,
|
|
||||||
mangle: false,
|
|
||||||
output: {
|
|
||||||
beautify: true,
|
|
||||||
braces: true,
|
|
||||||
comments: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (result.error) return {
|
|
||||||
code: testcase,
|
|
||||||
};
|
|
||||||
var toplevel = sandbox.has_toplevel(minify_options);
|
|
||||||
if (isNaN(timeout)) {
|
|
||||||
if (!U.minify(result.code, minify_options).error) return {
|
|
||||||
code: testcase,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
var actual = run_code(result.code, toplevel, result_cache, timeout).result;
|
|
||||||
if (!sandbox.same_stdout(expected, actual)) return {
|
|
||||||
code: testcase,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
result.code = "// (beautified)\n" + result.code;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function has_exit(fn) {
|
function has_exit(fn) {
|
||||||
var found = false;
|
var found = false;
|
||||||
var tw = new U.TreeWalker(function(node) {
|
var tw = new U.TreeWalker(function(node) {
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ exports.should_stop = function(callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function read(url, callback) {
|
function read(url, callback) {
|
||||||
|
var done = function(reply) {
|
||||||
|
done = function() {};
|
||||||
|
callback(reply);
|
||||||
|
};
|
||||||
var options = parse(url);
|
var options = parse(url);
|
||||||
options.headers = {
|
options.headers = {
|
||||||
"Authorization": "Token " + token,
|
"Authorization": "Token " + token,
|
||||||
@@ -49,7 +53,15 @@ function read(url, callback) {
|
|||||||
response.on("data", function(chunk) {
|
response.on("data", function(chunk) {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
}).on("end", function() {
|
}).on("end", function() {
|
||||||
callback(JSON.parse(chunks.join("")));
|
var reply;
|
||||||
|
try {
|
||||||
|
reply = JSON.parse(chunks.join(""))
|
||||||
|
} catch (e) {}
|
||||||
|
done(reply);
|
||||||
|
}).on("error", function() {
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
}).on("error", function() {
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,13 @@ function run() {
|
|||||||
function respawn() {
|
function respawn() {
|
||||||
console.log(stdout.replace(/[^\r\n]*\r/g, ""));
|
console.log(stdout.replace(/[^\r\n]*\r/g, ""));
|
||||||
clearInterval(log);
|
clearInterval(log);
|
||||||
if (!iterations) spawn();
|
if (!iterations) {
|
||||||
|
spawn();
|
||||||
|
} else if (process.exitCode) {
|
||||||
|
tasks.forEach(function(kill) {
|
||||||
|
kill();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function trap(data) {
|
function trap(data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user