Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6064bea3db | ||
|
|
98978fc827 | ||
|
|
16430acc1f | ||
|
|
320c110b33 | ||
|
|
dbe33bbfc5 | ||
|
|
b5c3253b49 | ||
|
|
5cc90db7d0 | ||
|
|
f427e5efc7 | ||
|
|
e48802ad29 | ||
|
|
13c4dfcabd |
@@ -287,9 +287,9 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
},
|
||||
wrap_commonjs: function(name, export_all) {
|
||||
var self = this;
|
||||
var to_export = [];
|
||||
if (export_all) {
|
||||
self.figure_out_scope();
|
||||
var to_export = [];
|
||||
self.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
|
||||
if (!find_if(function(n){ return n.name == node.name }, to_export))
|
||||
|
||||
100
lib/compress.js
100
lib/compress.js
@@ -883,18 +883,23 @@ merge(Compressor.prototype, {
|
||||
&& !self.uses_eval
|
||||
) {
|
||||
var in_use = [];
|
||||
var initializations = new Dictionary();
|
||||
// pass 1: find out which symbols are directly used in
|
||||
// this scope (not in nested scopes).
|
||||
var scope = this;
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node !== self) {
|
||||
if (node instanceof AST_Defun) {
|
||||
initializations.add(node.name.name, node);
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
node.definitions.forEach(function(def){
|
||||
if (def.value && def.value.has_side_effects()) {
|
||||
def.value.walk(tw);
|
||||
if (def.value) {
|
||||
initializations.add(def.name.name, def.value);
|
||||
if (def.value.has_side_effects()) {
|
||||
def.value.walk(tw);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@@ -919,16 +924,15 @@ merge(Compressor.prototype, {
|
||||
for (var i = 0; i < in_use.length; ++i) {
|
||||
in_use[i].orig.forEach(function(decl){
|
||||
// undeclared globals will be instanceof AST_SymbolRef
|
||||
if (decl instanceof AST_SymbolDeclaration) {
|
||||
decl.init.forEach(function(init){
|
||||
var tw = new TreeWalker(function(node){
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
push_uniq(in_use, node.definition());
|
||||
}
|
||||
});
|
||||
init.walk(tw);
|
||||
var init = initializations.get(decl.name);
|
||||
if (init) init.forEach(function(init){
|
||||
var tw = new TreeWalker(function(node){
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
push_uniq(in_use, node.definition());
|
||||
}
|
||||
});
|
||||
}
|
||||
init.walk(tw);
|
||||
});
|
||||
});
|
||||
}
|
||||
// pass 3: we should drop declarations not in_use
|
||||
@@ -1100,13 +1104,71 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
);
|
||||
self = self.transform(tt);
|
||||
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
||||
definitions: vars.map(function(def){
|
||||
def = def.clone();
|
||||
def.value = null;
|
||||
return def;
|
||||
})
|
||||
}));
|
||||
if (vars_found > 0) {
|
||||
// collect only vars which don't show up in self's arguments list
|
||||
var defs = [];
|
||||
vars.each(function(def, name){
|
||||
if (self instanceof AST_Lambda
|
||||
&& find_if(function(x){ return x.name == def.name.name },
|
||||
self.argnames)) {
|
||||
vars.del(name);
|
||||
} else {
|
||||
def = def.clone();
|
||||
def.value = null;
|
||||
defs.push(def);
|
||||
vars.set(name, def);
|
||||
}
|
||||
});
|
||||
if (defs.length > 0) {
|
||||
// try to merge in assignments
|
||||
for (var i = 0; i < self.body.length;) {
|
||||
if (self.body[i] instanceof AST_SimpleStatement) {
|
||||
var expr = self.body[i].body, sym, assign;
|
||||
if (expr instanceof AST_Assign
|
||||
&& expr.operator == "="
|
||||
&& (sym = expr.left) instanceof AST_Symbol
|
||||
&& vars.has(sym.name))
|
||||
{
|
||||
var def = vars.get(sym.name);
|
||||
if (def.value) break;
|
||||
def.value = expr.right;
|
||||
remove(defs, def);
|
||||
defs.push(def);
|
||||
self.body.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
if (expr instanceof AST_Seq
|
||||
&& (assign = expr.car) instanceof AST_Assign
|
||||
&& assign.operator == "="
|
||||
&& (sym = assign.left) instanceof AST_Symbol
|
||||
&& vars.has(sym.name))
|
||||
{
|
||||
var def = vars.get(sym.name);
|
||||
if (def.value) break;
|
||||
def.value = assign.right;
|
||||
remove(defs, def);
|
||||
defs.push(def);
|
||||
self.body[i].body = expr.cdr;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (self.body[i] instanceof AST_EmptyStatement) {
|
||||
self.body.splice(i, 1);
|
||||
continue;
|
||||
}
|
||||
if (self.body[i] instanceof AST_BlockStatement) {
|
||||
var tmp = [ i, 1 ].concat(self.body[i].body);
|
||||
self.body.splice.apply(self.body, tmp);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
defs = make_node(AST_Var, self, {
|
||||
definitions: defs
|
||||
});
|
||||
hoisted.push(defs);
|
||||
};
|
||||
}
|
||||
self.body = dirs.concat(hoisted, self.body);
|
||||
}
|
||||
return self;
|
||||
@@ -1430,7 +1492,7 @@ merge(Compressor.prototype, {
|
||||
return node;
|
||||
}
|
||||
});
|
||||
tt.stack = compressor.stack; // so that's able to see parent nodes
|
||||
tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
|
||||
self = self.transform(tt);
|
||||
} catch(ex) {
|
||||
if (ex !== self) throw ex;
|
||||
|
||||
@@ -340,10 +340,10 @@ function OutputStream(options) {
|
||||
/* -----[ utils ]----- */
|
||||
|
||||
function DEFPRINT(nodetype, generator) {
|
||||
nodetype.DEFMETHOD("print", function(stream){
|
||||
nodetype.DEFMETHOD("print", function(stream, force_parens){
|
||||
var self = this;
|
||||
stream.push_node(self);
|
||||
if (self.needs_parens(stream)) {
|
||||
if (force_parens || self.needs_parens(stream)) {
|
||||
stream.with_parens(function(){
|
||||
self.add_comments(stream);
|
||||
self.add_source_map(stream);
|
||||
@@ -467,18 +467,6 @@ function OutputStream(options) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// for (var i = (foo in bar);;); ← perhaps useless, but valid syntax
|
||||
if (this.operator == "in") {
|
||||
// the “NoIn” stuff :-\
|
||||
// UglifyJS 1.3.3 misses this one.
|
||||
if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this)
|
||||
return true;
|
||||
if (p instanceof AST_VarDef) {
|
||||
var v = output.parent(1), p2 = output.parent(2);
|
||||
if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
PARENS(AST_PropAccess, function(output){
|
||||
@@ -622,7 +610,11 @@ function OutputStream(options) {
|
||||
output.space();
|
||||
output.with_parens(function(){
|
||||
if (self.init) {
|
||||
self.init.print(output);
|
||||
if (self.init instanceof AST_Definitions) {
|
||||
self.init.print(output);
|
||||
} else {
|
||||
parenthesize_for_noin(self.init, output, true);
|
||||
}
|
||||
output.print(";");
|
||||
output.space();
|
||||
} else {
|
||||
@@ -866,13 +858,32 @@ function OutputStream(options) {
|
||||
DEFPRINT(AST_Const, function(self, output){
|
||||
self._do_print(output, "const");
|
||||
});
|
||||
|
||||
function parenthesize_for_noin(node, output, noin) {
|
||||
if (!noin) node.print(output);
|
||||
else try {
|
||||
// need to take some precautions here:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/60
|
||||
node.walk(new TreeWalker(function(node){
|
||||
if (node instanceof AST_Binary && node.operator == "in")
|
||||
throw output;
|
||||
}));
|
||||
node.print(output);
|
||||
} catch(ex) {
|
||||
if (ex !== output) throw ex;
|
||||
node.print(output, true);
|
||||
}
|
||||
};
|
||||
|
||||
DEFPRINT(AST_VarDef, function(self, output){
|
||||
self.name.print(output);
|
||||
if (self.value) {
|
||||
output.space();
|
||||
output.print("=");
|
||||
output.space();
|
||||
self.value.print(output);
|
||||
var p = output.parent(1);
|
||||
var noin = p instanceof AST_For || p instanceof AST_ForIn;
|
||||
parenthesize_for_noin(self.value, output, noin);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
11
lib/scope.js
11
lib/scope.js
@@ -110,9 +110,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
||||
labels.del(l.name);
|
||||
return true; // no descend again
|
||||
}
|
||||
if (node instanceof AST_SymbolDeclaration) {
|
||||
node.init_scope_vars();
|
||||
}
|
||||
if (node instanceof AST_Symbol) {
|
||||
node.scope = scope;
|
||||
}
|
||||
@@ -128,8 +125,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
||||
// scope. Don't like this fix but seems we can't do any
|
||||
// better. IE: please die. Please!
|
||||
(node.scope = scope.parent_scope).def_function(node);
|
||||
|
||||
node.init.push(tw.parent());
|
||||
}
|
||||
else if (node instanceof AST_SymbolDefun) {
|
||||
// Careful here, the scope where this should be defined is
|
||||
@@ -138,14 +133,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
||||
// instanceof AST_Scope) but we get to the symbol a bit
|
||||
// later.
|
||||
(node.scope = scope.parent_scope).def_function(node);
|
||||
node.init.push(tw.parent());
|
||||
}
|
||||
else if (node instanceof AST_SymbolVar
|
||||
|| node instanceof AST_SymbolConst) {
|
||||
var def = scope.def_variable(node);
|
||||
def.constant = node instanceof AST_SymbolConst;
|
||||
def = tw.parent();
|
||||
if (def.value) node.init.push(def);
|
||||
}
|
||||
else if (node instanceof AST_SymbolCatch) {
|
||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||
@@ -246,10 +239,6 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
||||
this.frame = this.scope.nesting - def.scope.nesting;
|
||||
});
|
||||
|
||||
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
||||
this.init = [];
|
||||
});
|
||||
|
||||
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
||||
this.references = [];
|
||||
});
|
||||
|
||||
@@ -255,6 +255,14 @@ Dictionary.prototype = {
|
||||
this._values["$" + key] = val;
|
||||
return this;
|
||||
},
|
||||
add: function(key, val) {
|
||||
if (this.has(key)) {
|
||||
this.get(key).push(val);
|
||||
} else {
|
||||
this.set(key, [ val ]);
|
||||
}
|
||||
return this;
|
||||
},
|
||||
get: function(key) { return this._values["$" + key] },
|
||||
del: function(key) {
|
||||
if (this.has(key)) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"main": "tools/node.js",
|
||||
"version": "2.2.1",
|
||||
"version": "2.2.2",
|
||||
"engines": { "node" : ">=0.4.0" },
|
||||
"maintainers": [{
|
||||
"name": "Mihai Bazon",
|
||||
|
||||
30
test/compress/issue-59.js
Normal file
30
test/compress/issue-59.js
Normal file
@@ -0,0 +1,30 @@
|
||||
keep_continue: {
|
||||
options = {
|
||||
dead_code: true,
|
||||
evaluate: true
|
||||
};
|
||||
input: {
|
||||
while (a) {
|
||||
if (b) {
|
||||
switch (true) {
|
||||
case c():
|
||||
d();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
f();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
while (a) {
|
||||
if (b) {
|
||||
switch (true) {
|
||||
case c():
|
||||
d();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
f();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user