Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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);
|
||||
} 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) {
|
||||
if (value) switch (key) {
|
||||
case "thedef":
|
||||
|
||||
41
lib/ast.js
41
lib/ast.js
@@ -412,33 +412,46 @@ var AST_With = DEFNODE("With", "expression", {
|
||||
|
||||
/* -----[ scope and functions ]----- */
|
||||
|
||||
var AST_Scope = DEFNODE("Scope", "cname enclosed uses_eval uses_with parent_scope functions variables make_def", {
|
||||
var AST_BlockScope = DEFNODE("BlockScope", "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",
|
||||
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",
|
||||
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
||||
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.variables) node.variables = this.variables.clone();
|
||||
if (this.functions) node.functions = this.functions.clone();
|
||||
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_Scope = DEFNODE("Scope", "cname uses_eval uses_with", {
|
||||
$documentation: "Base class for all statements introducing a lexical scope",
|
||||
$propdoc: {
|
||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||
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",
|
||||
},
|
||||
pinned: function() {
|
||||
return this.uses_eval || this.uses_with;
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.parent_scope != null) {
|
||||
if (!(this.parent_scope instanceof AST_Scope)) throw new Error("parent_scope must be AST_Scope");
|
||||
}
|
||||
},
|
||||
}, AST_Block);
|
||||
resolve: return_this,
|
||||
}, AST_BlockScope);
|
||||
|
||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
$documentation: "The toplevel scope",
|
||||
@@ -703,7 +716,7 @@ var AST_Catch = DEFNODE("Catch", "argname", {
|
||||
_validate: function() {
|
||||
if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
|
||||
},
|
||||
}, AST_Block);
|
||||
}, AST_BlockScope);
|
||||
|
||||
var AST_Finally = DEFNODE("Finally", null, {
|
||||
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
||||
|
||||
108
lib/compress.js
108
lib/compress.js
@@ -1037,18 +1037,6 @@ merge(Compressor.prototype, {
|
||||
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) {
|
||||
if (!props) props = {};
|
||||
if (orig) {
|
||||
@@ -2150,7 +2138,9 @@ merge(Compressor.prototype, {
|
||||
function handle_if_return(statements, compressor) {
|
||||
var self = compressor.self();
|
||||
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 multiple_if_returns = has_multiple_if_returns(statements);
|
||||
for (var i = statements.length; --i >= 0;) {
|
||||
@@ -2253,7 +2243,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
//---
|
||||
// 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;
|
||||
stat = stat.clone();
|
||||
stat.alternative = next;
|
||||
@@ -2323,14 +2313,21 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
}
|
||||
|
||||
function match_target(target) {
|
||||
function last_of(predicate) {
|
||||
var block = self, stat, level = 0;
|
||||
do {
|
||||
do {
|
||||
if (block === target) return true;
|
||||
if (predicate(block)) return true;
|
||||
block = compressor.parent(level++);
|
||||
} 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) {
|
||||
@@ -4168,13 +4165,7 @@ merge(Compressor.prototype, {
|
||||
var scopes = [];
|
||||
self.walk(new TreeWalker(function(node, descend) {
|
||||
if (!result) return true;
|
||||
if (node instanceof AST_Catch) {
|
||||
scopes.push(node.argname.scope);
|
||||
descend();
|
||||
scopes.pop();
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
if (node instanceof AST_BlockScope) {
|
||||
if (node === self) return;
|
||||
scopes.push(node);
|
||||
descend();
|
||||
@@ -4182,14 +4173,14 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
if (self.inlined) {
|
||||
if (self.inlined || node.redef) {
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
if (self.variables.has(node.name)) return true;
|
||||
var def = node.definition();
|
||||
if (member(def.scope, scopes)) return true;
|
||||
if (scope) {
|
||||
if (scope && !def.redefined()) {
|
||||
var scope_def = scope.find_variable(node);
|
||||
if (def.undeclared ? !scope_def : scope_def === def) {
|
||||
result = "f";
|
||||
@@ -4415,7 +4406,12 @@ merge(Compressor.prototype, {
|
||||
push();
|
||||
segment.block = node;
|
||||
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();
|
||||
pop();
|
||||
return true;
|
||||
@@ -4448,15 +4444,16 @@ merge(Compressor.prototype, {
|
||||
push();
|
||||
segment.block = node;
|
||||
walk_body(node, tw);
|
||||
pop();
|
||||
if (node.bcatch) {
|
||||
var def = node.bcatch.argname.definition();
|
||||
references[def.id] = false;
|
||||
if (def = def.redefined()) references[def.id] = false;
|
||||
pop();
|
||||
push();
|
||||
if (node.bfinally) segment.block = node.bcatch;
|
||||
walk_body(node.bcatch, tw);
|
||||
pop();
|
||||
}
|
||||
pop();
|
||||
if (node.bfinally) node.bfinally.walk(tw);
|
||||
return true;
|
||||
}
|
||||
@@ -4482,6 +4479,7 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
tw.directives = Object.create(compressor.directives);
|
||||
self.walk(tw);
|
||||
var merged = Object.create(null);
|
||||
while (first.length && last.length) {
|
||||
@@ -4792,15 +4790,15 @@ merge(Compressor.prototype, {
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
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;
|
||||
if (trim) {
|
||||
log(sym, "Dropping unused function argument {name}");
|
||||
a.pop();
|
||||
}
|
||||
} else {
|
||||
trim = false;
|
||||
if (indexOf_assign(def, sym) < 0) sym.__unused = null;
|
||||
}
|
||||
}
|
||||
fns_with_marked_args.push(node);
|
||||
@@ -4853,8 +4851,8 @@ merge(Compressor.prototype, {
|
||||
&& (old_def.name == def.name.name || all(old_def.references, function(ref) {
|
||||
return ref.scope.find_variable(def.name) === def.name.definition();
|
||||
})))
|
||||
&& can_rename(def.value, def.name.name)
|
||||
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
|
||||
&& can_declare_defun()
|
||||
&& can_rename(def.value, def.name.name)) {
|
||||
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
||||
var defun = make_node(AST_Defun, def, def.value);
|
||||
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
|
||||
@@ -4910,6 +4908,13 @@ merge(Compressor.prototype, {
|
||||
var def = fn.variables.get(name);
|
||||
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) {
|
||||
case 0:
|
||||
@@ -5163,6 +5168,13 @@ merge(Compressor.prototype, {
|
||||
if (!(node_def.id in in_use_ids)) {
|
||||
in_use_ids[node_def.id] = true;
|
||||
in_use.push(node_def);
|
||||
if (node.scope !== node_def.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);
|
||||
return true;
|
||||
@@ -5391,7 +5403,7 @@ merge(Compressor.prototype, {
|
||||
process_boolean_returns(this, compressor);
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("var_names", function() {
|
||||
AST_BlockScope.DEFMETHOD("var_names", function() {
|
||||
var var_names = this._var_names;
|
||||
if (!var_names) {
|
||||
this._var_names = var_names = Object.create(null);
|
||||
@@ -6804,7 +6816,7 @@ merge(Compressor.prototype, {
|
||||
&& !fn.pinned()
|
||||
&& !(fn.name && fn instanceof AST_Function)
|
||||
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
|
||||
&& fn.is_constant_expression(compressor.find_parent(AST_Scope)))
|
||||
&& fn.is_constant_expression(compressor.find_parent(AST_BlockScope)))
|
||||
&& (value = can_flatten_body(stat))
|
||||
&& !fn.contains_this()) {
|
||||
var replacing = exp === fn || compressor.option("unused") && def.references.length - def.replaced == 1;
|
||||
@@ -6830,7 +6842,7 @@ merge(Compressor.prototype, {
|
||||
return arg;
|
||||
})).optimize(compressor);
|
||||
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) {
|
||||
var def = ref.definition();
|
||||
def.references.push(ref);
|
||||
@@ -7046,9 +7058,10 @@ merge(Compressor.prototype, {
|
||||
value: null
|
||||
}));
|
||||
}
|
||||
if (!value) return;
|
||||
var sym = make_node(AST_SymbolRef, name, name);
|
||||
def.references.push(sym);
|
||||
if (value) expressions.push(make_node(AST_Assign, self, {
|
||||
expressions.push(make_node(AST_Assign, self, {
|
||||
operator: "=",
|
||||
left: sym,
|
||||
right: value
|
||||
@@ -7069,7 +7082,12 @@ merge(Compressor.prototype, {
|
||||
var symbol = make_node(AST_SymbolVar, name, name);
|
||||
name.definition().orig.push(symbol);
|
||||
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();
|
||||
@@ -8065,7 +8083,9 @@ merge(Compressor.prototype, {
|
||||
single_use = false;
|
||||
} else if (fixed.name && fixed.name.definition() !== def) {
|
||||
single_use = false;
|
||||
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
|
||||
} else if (fixed.parent_scope !== self.scope
|
||||
|| !(self.scope instanceof AST_Scope)
|
||||
|| def.orig[0] instanceof AST_SymbolFunarg) {
|
||||
single_use = fixed.is_constant_expression(self.scope);
|
||||
if (single_use == "f") {
|
||||
var scope = self.scope;
|
||||
@@ -8213,7 +8233,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Undefined, function(self, compressor) {
|
||||
if (compressor.option("unsafe_undefined")) {
|
||||
var undef = find_variable(compressor, "undefined");
|
||||
var undef = compressor.find_parent(AST_BlockScope).find_variable("undefined");
|
||||
if (undef) {
|
||||
var ref = make_node(AST_SymbolRef, self, {
|
||||
name : "undefined",
|
||||
@@ -8239,7 +8259,7 @@ merge(Compressor.prototype, {
|
||||
if (lhs && is_atomic(lhs, self)) return self;
|
||||
if (compressor.option("keep_infinity")
|
||||
&& !(lhs && !is_atomic(lhs, self))
|
||||
&& !find_variable(compressor, "Infinity"))
|
||||
&& !compressor.find_parent(AST_BlockScope).find_variable("Infinity"))
|
||||
return self;
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "/",
|
||||
@@ -8255,7 +8275,7 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_NaN, function(self, compressor) {
|
||||
var lhs = is_lhs(compressor.self(), compressor.parent());
|
||||
if (lhs && !is_atomic(lhs, self)
|
||||
|| find_variable(compressor, "NaN")) {
|
||||
|| compressor.find_parent(AST_BlockScope).find_variable("NaN")) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: "/",
|
||||
left: make_node(AST_Number, self, {
|
||||
|
||||
117
lib/scope.js
117
lib/scope.js
@@ -59,13 +59,9 @@ function SymbolDef(id, scope, orig, init) {
|
||||
}
|
||||
|
||||
SymbolDef.prototype = {
|
||||
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);
|
||||
forEach: function(fn) {
|
||||
this.orig.forEach(fn);
|
||||
this.references.forEach(fn);
|
||||
},
|
||||
mangle: function(options) {
|
||||
var cache = options.cache && options.cache.props;
|
||||
@@ -85,7 +81,15 @@ SymbolDef.prototype = {
|
||||
},
|
||||
redefined: function() {
|
||||
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) {
|
||||
@@ -100,22 +104,18 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
var next_def_id = 0;
|
||||
var scope = self.parent_scope = null;
|
||||
var tw = new TreeWalker(function(node, descend) {
|
||||
if (node instanceof AST_Catch) {
|
||||
var save_scope = scope;
|
||||
scope = new AST_Scope(node);
|
||||
scope.init_scope_vars(save_scope);
|
||||
descend();
|
||||
scope = save_scope;
|
||||
if (node instanceof AST_Defun) {
|
||||
node.name.walk(tw);
|
||||
walk_scope(function() {
|
||||
node.argnames.forEach(function(argname) {
|
||||
argname.walk(tw);
|
||||
});
|
||||
walk_body(node, tw);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope) {
|
||||
node.init_scope_vars(scope);
|
||||
var save_scope = scope;
|
||||
var save_defun = defun;
|
||||
defun = scope = node;
|
||||
descend();
|
||||
scope = save_scope;
|
||||
defun = save_defun;
|
||||
if (node instanceof AST_BlockScope) {
|
||||
walk_scope(descend);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_With) {
|
||||
@@ -129,25 +129,41 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
node.thedef = node;
|
||||
node.references = [];
|
||||
}
|
||||
if (node instanceof AST_SymbolDefun) {
|
||||
// This should be defined in the parent scope, as we encounter the
|
||||
// AST_Defun node before getting to its AST_Symbol.
|
||||
(node.scope = defun.parent_scope.resolve()).def_function(node, defun);
|
||||
if (node instanceof AST_SymbolCatch) {
|
||||
scope.def_variable(node).defun = defun;
|
||||
} else if (node instanceof AST_SymbolDefun) {
|
||||
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) {
|
||||
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
|
||||
if (options.ie8) def.defun = defun.parent_scope.resolve();
|
||||
} else if (node instanceof AST_SymbolVar) {
|
||||
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
|
||||
if (defun !== scope) {
|
||||
node.mark_enclosed(options);
|
||||
var def = scope.find_variable(node);
|
||||
if (node.thedef !== def) {
|
||||
node.thedef = def;
|
||||
}
|
||||
node.reference(options);
|
||||
}
|
||||
} else if (node instanceof AST_SymbolCatch) {
|
||||
scope.def_variable(node).defun = defun;
|
||||
defun.def_variable(node, null);
|
||||
entangle(defun, scope);
|
||||
}
|
||||
|
||||
function walk_scope(descend) {
|
||||
node.init_scope_vars(scope);
|
||||
var save_defun = defun;
|
||||
var save_scope = scope;
|
||||
if (node instanceof AST_Scope) defun = node;
|
||||
scope = node;
|
||||
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) {
|
||||
@@ -234,7 +250,8 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
new_def = scope.def_variable(node);
|
||||
}
|
||||
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.reference(options);
|
||||
});
|
||||
@@ -267,7 +284,7 @@ function init_scope_vars(scope, parent) {
|
||||
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
|
||||
}
|
||||
|
||||
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
||||
AST_BlockScope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
||||
init_scope_vars(this, parent_scope);
|
||||
});
|
||||
|
||||
@@ -300,20 +317,20 @@ AST_Symbol.DEFMETHOD("reference", function(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;
|
||||
return this.variables.get(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);
|
||||
if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
||||
this.functions.set(symbol.name, 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);
|
||||
if (def) {
|
||||
def.orig.push(symbol);
|
||||
@@ -326,12 +343,6 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
|
||||
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) {
|
||||
var names = scope.names_in_use;
|
||||
if (!names) {
|
||||
@@ -353,7 +364,7 @@ function next_mangled_name(scope, options, def) {
|
||||
var holes = scope.cname_holes;
|
||||
var names = Object.create(null);
|
||||
var scopes = [ scope ];
|
||||
def.references.forEach(function(sym) {
|
||||
def.forEach(function(sym) {
|
||||
var scope = sym.scope;
|
||||
do {
|
||||
if (scopes.indexOf(scope) < 0) {
|
||||
@@ -495,8 +506,7 @@ AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) {
|
||||
options.reserved.forEach(to_avoid);
|
||||
this.globals.each(add_def);
|
||||
this.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Scope) node.variables.each(add_def);
|
||||
if (node instanceof AST_SymbolCatch) add_def(node.definition());
|
||||
if (node instanceof AST_BlockScope) node.variables.each(add_def);
|
||||
}));
|
||||
return avoid;
|
||||
|
||||
@@ -520,8 +530,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
||||
var cname = 0;
|
||||
this.globals.each(rename);
|
||||
this.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Scope) node.variables.each(rename);
|
||||
if (node instanceof AST_SymbolCatch) rename(node.definition());
|
||||
if (node instanceof AST_BlockScope) node.variables.each(rename);
|
||||
}));
|
||||
|
||||
function next_name() {
|
||||
@@ -539,7 +548,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
|
||||
var redef = def.redefined();
|
||||
var name = redef ? redef.rename || redef.name : next_name();
|
||||
def.rename = name;
|
||||
def.orig.concat(def.references).forEach(function(sym) {
|
||||
def.forEach(function(sym) {
|
||||
if (sym.definition() === def) sym.name = name;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.11.0",
|
||||
"version": "3.11.1",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -2992,3 +2992,34 @@ issue_4146: {
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4808,3 +4808,182 @@ issue_4155: {
|
||||
"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
|
||||
}
|
||||
|
||||
@@ -2714,3 +2714,108 @@ issue_2737: {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -663,3 +663,88 @@ nested_if_continue: {
|
||||
"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() {
|
||||
// NOTE: other compression steps will reduce this
|
||||
// down to just `window`.
|
||||
if ( window );
|
||||
if ( !window );
|
||||
function f() {}
|
||||
function g() {}
|
||||
} )();
|
||||
@@ -38,7 +38,7 @@ single_function: {
|
||||
}
|
||||
expect: {
|
||||
( function() {
|
||||
if ( window );
|
||||
if ( !window );
|
||||
function f() {}
|
||||
} )();
|
||||
}
|
||||
@@ -67,7 +67,7 @@ deeply_nested: {
|
||||
// NOTE: other compression steps will reduce this
|
||||
// down to just `window`.
|
||||
if ( window )
|
||||
if (document);
|
||||
if ( !document );
|
||||
function f() {}
|
||||
function g() {}
|
||||
function h() {}
|
||||
|
||||
@@ -2617,9 +2617,9 @@ issue_4126_1: {
|
||||
try {
|
||||
console.log("PASS");
|
||||
} catch (e) {
|
||||
var c = a;
|
||||
var b = a;
|
||||
} finally {
|
||||
var c = c;
|
||||
var c = b;
|
||||
}
|
||||
console.log(c);
|
||||
}
|
||||
@@ -2860,3 +2860,151 @@ issue_4155: {
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -5374,11 +5374,11 @@ defun_catch_4: {
|
||||
try {
|
||||
throw 42;
|
||||
} catch (a) {
|
||||
function a() {}
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: "<=4"
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
defun_catch_5: {
|
||||
@@ -5400,10 +5400,10 @@ defun_catch_5: {
|
||||
throw 42;
|
||||
} catch (a) {
|
||||
console.log(a);
|
||||
function a() {}
|
||||
}
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: "<=4"
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
defun_catch_6: {
|
||||
|
||||
@@ -38,6 +38,10 @@ exports.should_stop = function(callback) {
|
||||
};
|
||||
|
||||
function read(url, callback) {
|
||||
var done = function(reply) {
|
||||
done = function() {};
|
||||
callback(reply);
|
||||
};
|
||||
var options = parse(url);
|
||||
options.headers = {
|
||||
"Authorization": "Token " + token,
|
||||
@@ -49,7 +53,15 @@ function read(url, callback) {
|
||||
response.on("data", function(chunk) {
|
||||
chunks.push(chunk);
|
||||
}).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() {
|
||||
console.log(stdout.replace(/[^\r\n]*\r/g, ""));
|
||||
clearInterval(log);
|
||||
if (!iterations) spawn();
|
||||
if (!iterations) {
|
||||
spawn();
|
||||
} else if (process.exitCode) {
|
||||
tasks.forEach(function(kill) {
|
||||
kill();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function trap(data) {
|
||||
|
||||
Reference in New Issue
Block a user