Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64270b9778 | ||
|
|
e312c5c2a7 | ||
|
|
1a5fd3e052 | ||
|
|
5a7e54cf72 | ||
|
|
39f8a62703 | ||
|
|
46be3f2bf1 | ||
|
|
258b46f4dc | ||
|
|
80da21dab4 | ||
|
|
bb0e4d7126 | ||
|
|
5276a4a873 | ||
|
|
a1ae0c8609 | ||
|
|
a90c1aeafe | ||
|
|
ff388a8d2d | ||
|
|
5346fb94bb | ||
|
|
a4f6d46118 | ||
|
|
7f5f4d60b7 | ||
|
|
ffccb233e5 | ||
|
|
fba0c1aafe | ||
|
|
774f2ded94 | ||
|
|
85af942d64 | ||
|
|
8413787efc | ||
|
|
dde57452aa | ||
|
|
cf409800be | ||
|
|
18270dd9f3 | ||
|
|
d4c25c571b | ||
|
|
5248b79506 | ||
|
|
abe0ebbf02 | ||
|
|
0852f5595e | ||
|
|
cb3cafa14d | ||
|
|
202fb93799 | ||
|
|
7b87d2ef83 | ||
|
|
70fd2b1f33 | ||
|
|
30faaf13ed | ||
|
|
41be8632d3 | ||
|
|
bee01dc1be | ||
|
|
12f71e01d0 | ||
|
|
3a72deacab | ||
|
|
fc8314e810 | ||
|
|
11dffe950e | ||
|
|
6f45928a73 | ||
|
|
afb7faa6fa |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
tmp/
|
tmp/
|
||||||
|
node_modules/
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -322,6 +322,7 @@ There's a single toplevel function which combines all the steps. If you
|
|||||||
don't need additional customization, you might want to go with `minify`.
|
don't need additional customization, you might want to go with `minify`.
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
// see "fromString" below if you need to pass code instead of file name
|
||||||
var result = UglifyJS.minify("/path/to/file.js");
|
var result = UglifyJS.minify("/path/to/file.js");
|
||||||
console.log(result.code); // minified output
|
console.log(result.code); // minified output
|
||||||
|
|
||||||
@@ -342,6 +343,14 @@ Note that the source map is not saved in a file, it's just returned in
|
|||||||
`result.map`. The value passed for `outSourceMap` is only used to set the
|
`result.map`. The value passed for `outSourceMap` is only used to set the
|
||||||
`file` attribute in the source map (see [the spec][sm-spec]).
|
`file` attribute in the source map (see [the spec][sm-spec]).
|
||||||
|
|
||||||
|
You can also specify sourceRoot property to be included in source map:
|
||||||
|
|
||||||
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
|
outSourceMap: "out.js.map",
|
||||||
|
sourceRoot: "http://example.com/src"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
If you're compressing compiled JavaScript and have a source map for it, you
|
If you're compressing compiled JavaScript and have a source map for it, you
|
||||||
can use the `inSourceMap` argument:
|
can use the `inSourceMap` argument:
|
||||||
|
|
||||||
@@ -354,6 +363,12 @@ can use the `inSourceMap` argument:
|
|||||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||||
no sense otherwise).
|
no sense otherwise).
|
||||||
|
|
||||||
|
Other options:
|
||||||
|
|
||||||
|
- `warnings` (default `false`) — pass `true` to display compressor warnings.
|
||||||
|
- `fromString` (default `false`) — if you pass `true` then you can pass
|
||||||
|
JavaScript source code, rather than file names.
|
||||||
|
|
||||||
We could add more options to `UglifyJS.minify` — if you need additional
|
We could add more options to `UglifyJS.minify` — if you need additional
|
||||||
functionality please suggest!
|
functionality please suggest!
|
||||||
|
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ if (SCOPE_IS_NEEDED) {
|
|||||||
time_it("scope", function(){
|
time_it("scope", function(){
|
||||||
TOPLEVEL.figure_out_scope();
|
TOPLEVEL.figure_out_scope();
|
||||||
if (MANGLE) {
|
if (MANGLE) {
|
||||||
TOPLEVEL.compute_char_frequency();
|
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
28
lib/ast.js
28
lib/ast.js
@@ -345,6 +345,10 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
|||||||
}
|
}
|
||||||
}, AST_Scope);
|
}, AST_Scope);
|
||||||
|
|
||||||
|
var AST_Accessor = DEFNODE("Accessor", null, {
|
||||||
|
$documentation: "A setter/getter function"
|
||||||
|
}, AST_Lambda);
|
||||||
|
|
||||||
var AST_Function = DEFNODE("Function", null, {
|
var AST_Function = DEFNODE("Function", null, {
|
||||||
$documentation: "A function expression"
|
$documentation: "A function expression"
|
||||||
}, AST_Lambda);
|
}, AST_Lambda);
|
||||||
@@ -581,6 +585,18 @@ var AST_Seq = DEFNODE("Seq", "car cdr", {
|
|||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
},
|
},
|
||||||
|
to_array: function() {
|
||||||
|
var p = this, a = [];
|
||||||
|
while (p) {
|
||||||
|
a.push(p.car);
|
||||||
|
if (p.cdr && !(p.cdr instanceof AST_Seq)) {
|
||||||
|
a.push(p.cdr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = p.cdr;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
},
|
||||||
add: function(node) {
|
add: function(node) {
|
||||||
var p = this;
|
var p = this;
|
||||||
while (p) {
|
while (p) {
|
||||||
@@ -746,6 +762,10 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
|||||||
$documentation: "Base class for all symbols",
|
$documentation: "Base class for all symbols",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
||||||
|
$documentation: "The name of a property accessor (setter/getter function)"
|
||||||
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||||
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
$documentation: "A declaration symbol (symbol in var/const, function name or argument, symbol in catch)",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
@@ -929,10 +949,10 @@ TreeWalker.prototype = {
|
|||||||
} else {
|
} else {
|
||||||
for (var i = stack.length; --i >= 0;) {
|
for (var i = stack.length; --i >= 0;) {
|
||||||
var x = stack[i];
|
var x = stack[i];
|
||||||
if (x instanceof AST_Switch) return x;
|
if (x instanceof AST_Switch
|
||||||
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
|| x instanceof AST_For
|
||||||
return (x.body instanceof AST_BlockStatement ? x.body : x);
|
|| x instanceof AST_ForIn
|
||||||
}
|
|| x instanceof AST_DWLoop) return x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
238
lib/compress.js
238
lib/compress.js
@@ -53,6 +53,7 @@ function Compressor(options, false_by_default) {
|
|||||||
dead_code : !false_by_default,
|
dead_code : !false_by_default,
|
||||||
drop_debugger : !false_by_default,
|
drop_debugger : !false_by_default,
|
||||||
unsafe : !false_by_default,
|
unsafe : !false_by_default,
|
||||||
|
unsafe_comps : false,
|
||||||
conditionals : !false_by_default,
|
conditionals : !false_by_default,
|
||||||
comparisons : !false_by_default,
|
comparisons : !false_by_default,
|
||||||
evaluate : !false_by_default,
|
evaluate : !false_by_default,
|
||||||
@@ -186,6 +187,14 @@ merge(Compressor.prototype, {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function loop_body(x) {
|
||||||
|
if (x instanceof AST_Switch) return x;
|
||||||
|
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
||||||
|
return (x.body instanceof AST_BlockStatement ? x.body : x);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
function tighten_body(statements, compressor) {
|
function tighten_body(statements, compressor) {
|
||||||
var CHANGED;
|
var CHANGED;
|
||||||
do {
|
do {
|
||||||
@@ -303,8 +312,13 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ab = aborts(stat.body);
|
var ab = aborts(stat.body);
|
||||||
|
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
|
||||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||||
|| (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) {
|
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||||
|
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||||
|
if (ab.label) {
|
||||||
|
remove(ab.label.thedef.references, ab.label);
|
||||||
|
}
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
var body = as_statement_array(stat.body).slice(0, -1);
|
var body = as_statement_array(stat.body).slice(0, -1);
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
@@ -320,8 +334,13 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ab = aborts(stat.alternative);
|
var ab = aborts(stat.alternative);
|
||||||
|
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
|
||||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||||
|| (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) {
|
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||||
|
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||||
|
if (ab.label) {
|
||||||
|
remove(ab.label.thedef.references, ab.label);
|
||||||
|
}
|
||||||
CHANGED = true;
|
CHANGED = true;
|
||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
stat.body = make_node(AST_BlockStatement, stat.body, {
|
stat.body = make_node(AST_BlockStatement, stat.body, {
|
||||||
@@ -347,11 +366,26 @@ merge(Compressor.prototype, {
|
|||||||
function eliminate_dead_code(statements, compressor) {
|
function eliminate_dead_code(statements, compressor) {
|
||||||
var has_quit = false;
|
var has_quit = false;
|
||||||
var orig = statements.length;
|
var orig = statements.length;
|
||||||
|
var self = compressor.self();
|
||||||
statements = statements.reduce(function(a, stat){
|
statements = statements.reduce(function(a, stat){
|
||||||
if (has_quit) {
|
if (has_quit) {
|
||||||
extract_declarations_from_unreachable_code(compressor, stat, a);
|
extract_declarations_from_unreachable_code(compressor, stat, a);
|
||||||
} else {
|
} else {
|
||||||
a.push(stat);
|
if (stat instanceof AST_LoopControl) {
|
||||||
|
var lct = compressor.loopcontrol_target(stat.label);
|
||||||
|
if ((stat instanceof AST_Break
|
||||||
|
&& lct instanceof AST_BlockStatement
|
||||||
|
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
||||||
|
&& loop_body(lct) === self)) {
|
||||||
|
if (stat.label) {
|
||||||
|
remove(stat.label.thedef.references, stat.label);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a.push(stat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a.push(stat);
|
||||||
|
}
|
||||||
if (aborts(stat)) has_quit = true;
|
if (aborts(stat)) has_quit = true;
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
@@ -394,12 +428,23 @@ merge(Compressor.prototype, {
|
|||||||
var ret = [], prev = null;
|
var ret = [], prev = null;
|
||||||
statements.forEach(function(stat){
|
statements.forEach(function(stat){
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (stat instanceof AST_For && stat.init && !(stat.init instanceof AST_Definitions)) {
|
if (stat instanceof AST_For) {
|
||||||
stat.init = cons_seq(stat.init);
|
var opera = {};
|
||||||
}
|
try {
|
||||||
else if (stat instanceof AST_For && !stat.init) {
|
prev.body.walk(new TreeWalker(function(node){
|
||||||
stat.init = prev.body;
|
if (node instanceof AST_Binary && node.operator == "in")
|
||||||
ret.pop();
|
throw opera;
|
||||||
|
}));
|
||||||
|
if (stat.init && !(stat.init instanceof AST_Definitions)) {
|
||||||
|
stat.init = cons_seq(stat.init);
|
||||||
|
}
|
||||||
|
else if (!stat.init) {
|
||||||
|
stat.init = prev.body;
|
||||||
|
ret.pop();
|
||||||
|
}
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== opera) throw ex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (stat instanceof AST_If) {
|
else if (stat instanceof AST_If) {
|
||||||
stat.condition = cons_seq(stat.condition);
|
stat.condition = cons_seq(stat.condition);
|
||||||
@@ -659,7 +704,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
def(AST_Binary, function(compressor){
|
def(AST_Binary, function(compressor){
|
||||||
var self = this.clone(), op = this.operator;
|
var self = this.clone(), op = this.operator;
|
||||||
if (compressor.option("comparisons") && compressor.option("unsafe")) {
|
if (compressor.option("unsafe_comps")) {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case "<=" : self.operator = ">" ; return self;
|
case "<=" : self.operator = ">" ; return self;
|
||||||
case "<" : self.operator = ">=" ; return self;
|
case "<" : self.operator = ">=" ; return self;
|
||||||
@@ -708,9 +753,10 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
def(AST_SimpleStatement, function(){
|
def(AST_SimpleStatement, function(){
|
||||||
if (this.body instanceof AST_Function) return false;
|
|
||||||
return this.body.has_side_effects();
|
return this.body.has_side_effects();
|
||||||
});
|
});
|
||||||
|
def(AST_Defun, function(){ return true });
|
||||||
|
def(AST_Function, function(){ return false });
|
||||||
def(AST_Binary, function(){
|
def(AST_Binary, function(){
|
||||||
return this.left.has_side_effects()
|
return this.left.has_side_effects()
|
||||||
|| this.right.has_side_effects();
|
|| this.right.has_side_effects();
|
||||||
@@ -795,6 +841,10 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_LabeledStatement, function(self, compressor){
|
OPT(AST_LabeledStatement, function(self, compressor){
|
||||||
|
if (self.body instanceof AST_Break
|
||||||
|
&& compressor.loopcontrol_target(self.body.label) === self.body) {
|
||||||
|
return make_node(AST_EmptyStatement, self);
|
||||||
|
}
|
||||||
return self.label.references.length == 0 ? self.body : self;
|
return self.label.references.length == 0 ? self.body : self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -976,7 +1026,7 @@ merge(Compressor.prototype, {
|
|||||||
if (hoist_funs || hoist_vars) {
|
if (hoist_funs || hoist_vars) {
|
||||||
var dirs = [];
|
var dirs = [];
|
||||||
var hoisted = [];
|
var hoisted = [];
|
||||||
var vars = {}, vars_found = 0, var_decl = 0;
|
var vars = new Dictionary(), vars_found = 0, var_decl = 0;
|
||||||
// let's count var_decl first, we seem to waste a lot of
|
// let's count var_decl first, we seem to waste a lot of
|
||||||
// space if we hoist `var` when there's only one.
|
// space if we hoist `var` when there's only one.
|
||||||
self.walk(new TreeWalker(function(node){
|
self.walk(new TreeWalker(function(node){
|
||||||
@@ -1001,7 +1051,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Var && hoist_vars) {
|
if (node instanceof AST_Var && hoist_vars) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
vars[def.name.name] = def;
|
vars.set(def.name.name, def);
|
||||||
++vars_found;
|
++vars_found;
|
||||||
});
|
});
|
||||||
var seq = node.to_assignments();
|
var seq = node.to_assignments();
|
||||||
@@ -1025,8 +1075,8 @@ merge(Compressor.prototype, {
|
|||||||
);
|
);
|
||||||
self = self.transform(tt);
|
self = self.transform(tt);
|
||||||
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
||||||
definitions: Object.keys(vars).map(function(name){
|
definitions: vars.map(function(def){
|
||||||
var def = vars[name].clone();
|
def = def.clone();
|
||||||
def.value = null;
|
def.value = null;
|
||||||
return def;
|
return def;
|
||||||
})
|
})
|
||||||
@@ -1068,6 +1118,61 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function if_break_in_loop(self, compressor) {
|
||||||
|
function drop_it(rest) {
|
||||||
|
rest = as_statement_array(rest);
|
||||||
|
if (self.body instanceof AST_BlockStatement) {
|
||||||
|
self.body = self.body.clone();
|
||||||
|
self.body.body = rest.concat(self.body.body.slice(1));
|
||||||
|
self.body = self.body.transform(compressor);
|
||||||
|
} else {
|
||||||
|
self.body = make_node(AST_BlockStatement, self.body, {
|
||||||
|
body: rest
|
||||||
|
}).transform(compressor);
|
||||||
|
}
|
||||||
|
if_break_in_loop(self, compressor);
|
||||||
|
}
|
||||||
|
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
|
||||||
|
if (first instanceof AST_If) {
|
||||||
|
if (first.body instanceof AST_Break
|
||||||
|
&& compressor.loopcontrol_target(first.body.label) === self) {
|
||||||
|
if (self.condition) {
|
||||||
|
self.condition = make_node(AST_Binary, self.condition, {
|
||||||
|
left: self.condition,
|
||||||
|
operator: "&&",
|
||||||
|
right: first.condition.negate(compressor),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.condition = first.condition.negate(compressor);
|
||||||
|
}
|
||||||
|
drop_it(first.alternative);
|
||||||
|
}
|
||||||
|
else if (first.alternative instanceof AST_Break
|
||||||
|
&& compressor.loopcontrol_target(first.alternative.label) === self) {
|
||||||
|
if (self.condition) {
|
||||||
|
self.condition = make_node(AST_Binary, self.condition, {
|
||||||
|
left: self.condition,
|
||||||
|
operator: "&&",
|
||||||
|
right: first.condition,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.condition = first.condition;
|
||||||
|
}
|
||||||
|
drop_it(first.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OPT(AST_While, function(self, compressor) {
|
||||||
|
if (!compressor.option("loops")) return self;
|
||||||
|
self = AST_DWLoop.prototype.optimize.call(self, compressor);
|
||||||
|
if (self instanceof AST_While) {
|
||||||
|
if_break_in_loop(self, compressor);
|
||||||
|
self = make_node(AST_For, self, self).transform(compressor);
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_For, function(self, compressor){
|
OPT(AST_For, function(self, compressor){
|
||||||
var cond = self.condition;
|
var cond = self.condition;
|
||||||
if (cond) {
|
if (cond) {
|
||||||
@@ -1092,6 +1197,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if_break_in_loop(self, compressor);
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1182,8 +1288,8 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(self.body.CTOR, self, {
|
return make_node(self.body.CTOR, self, {
|
||||||
value: make_node(AST_Conditional, self, {
|
value: make_node(AST_Conditional, self, {
|
||||||
condition : self.condition,
|
condition : self.condition,
|
||||||
consequent : self.body.value,
|
consequent : self.body.value || make_node(AST_Undefined, self.body).optimize(compressor),
|
||||||
alternative : self.alternative.value || make_node(AST_Undefined, self).optimize(compressor)
|
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).optimize(compressor)
|
||||||
})
|
})
|
||||||
}).transform(compressor);
|
}).transform(compressor);
|
||||||
}
|
}
|
||||||
@@ -1227,7 +1333,7 @@ merge(Compressor.prototype, {
|
|||||||
var last_branch = self.body[self.body.length - 1];
|
var last_branch = self.body[self.body.length - 1];
|
||||||
if (last_branch) {
|
if (last_branch) {
|
||||||
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
||||||
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self)
|
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
||||||
last_branch.body.pop();
|
last_branch.body.pop();
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -1317,7 +1423,7 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("side_effects")) {
|
if (compressor.option("side_effects")) {
|
||||||
if (self.expression instanceof AST_Function
|
if (self.expression instanceof AST_Function
|
||||||
&& self.args.length == 0
|
&& self.args.length == 0
|
||||||
&& !self.expression.has_side_effects()) {
|
&& !AST_Block.prototype.has_side_effects.call(self.expression)) {
|
||||||
return make_node(AST_Undefined, self).transform(compressor);
|
return make_node(AST_Undefined, self).transform(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1342,6 +1448,10 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
OPT(AST_Seq, function(self, compressor){
|
OPT(AST_Seq, function(self, compressor){
|
||||||
|
if (!compressor.option("side_effects"))
|
||||||
|
return self;
|
||||||
|
if (!self.car.has_side_effects())
|
||||||
|
return self.cdr;
|
||||||
if (compressor.option("cascade")) {
|
if (compressor.option("cascade")) {
|
||||||
if (self.car instanceof AST_Assign
|
if (self.car instanceof AST_Assign
|
||||||
&& !self.car.left.has_side_effects()
|
&& !self.car.left.has_side_effects()
|
||||||
@@ -1357,7 +1467,26 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
|
||||||
|
if (compressor.option("sequences")) {
|
||||||
|
if (this.expression instanceof AST_Seq) {
|
||||||
|
var seq = this.expression;
|
||||||
|
var x = seq.to_array();
|
||||||
|
this.expression = x.pop();
|
||||||
|
x.push(this);
|
||||||
|
seq = AST_Seq.from_array(x).transform(compressor);
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
|
OPT(AST_UnaryPostfix, function(self, compressor){
|
||||||
|
return self.lift_sequences(compressor);
|
||||||
|
});
|
||||||
|
|
||||||
OPT(AST_UnaryPrefix, function(self, compressor){
|
OPT(AST_UnaryPrefix, function(self, compressor){
|
||||||
|
self = self.lift_sequences(compressor);
|
||||||
var e = self.expression;
|
var e = self.expression;
|
||||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||||
switch (self.operator) {
|
switch (self.operator) {
|
||||||
@@ -1373,14 +1502,53 @@ merge(Compressor.prototype, {
|
|||||||
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||||
return make_node(AST_True, self);
|
return make_node(AST_True, self);
|
||||||
}
|
}
|
||||||
}
|
if (e instanceof AST_Binary && self.operator == "!") {
|
||||||
if (e instanceof AST_Binary) {
|
self = best_of(self, e.negate(compressor));
|
||||||
self = best_of(self, e.negate(compressor));
|
}
|
||||||
}
|
}
|
||||||
return self.evaluate(compressor)[0];
|
return self.evaluate(compressor)[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AST_Binary.DEFMETHOD("lift_sequences", function(compressor){
|
||||||
|
if (compressor.option("sequences")) {
|
||||||
|
if (this.left instanceof AST_Seq) {
|
||||||
|
var seq = this.left;
|
||||||
|
var x = seq.to_array();
|
||||||
|
this.left = x.pop();
|
||||||
|
x.push(this);
|
||||||
|
seq = AST_Seq.from_array(x).transform(compressor);
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
if (this.right instanceof AST_Seq
|
||||||
|
&& !(this.operator == "||" || this.operator == "&&")
|
||||||
|
&& !this.left.has_side_effects()) {
|
||||||
|
var seq = this.right;
|
||||||
|
var x = seq.to_array();
|
||||||
|
this.right = x.pop();
|
||||||
|
x.push(this);
|
||||||
|
seq = AST_Seq.from_array(x).transform(compressor);
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
});
|
||||||
|
|
||||||
|
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor){
|
OPT(AST_Binary, function(self, compressor){
|
||||||
|
function reverse(op) {
|
||||||
|
if (op) self.operator = op;
|
||||||
|
var tmp = self.left;
|
||||||
|
self.left = self.right;
|
||||||
|
self.right = tmp;
|
||||||
|
};
|
||||||
|
if (commutativeOperators(self.operator)) {
|
||||||
|
if (self.right instanceof AST_Constant
|
||||||
|
&& !(self.left instanceof AST_Constant)) {
|
||||||
|
reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self = self.lift_sequences(compressor);
|
||||||
if (compressor.option("comparisons")) switch (self.operator) {
|
if (compressor.option("comparisons")) switch (self.operator) {
|
||||||
case "===":
|
case "===":
|
||||||
case "!==":
|
case "!==":
|
||||||
@@ -1391,21 +1559,10 @@ merge(Compressor.prototype, {
|
|||||||
// XXX: intentionally falling down to the next case
|
// XXX: intentionally falling down to the next case
|
||||||
case "==":
|
case "==":
|
||||||
case "!=":
|
case "!=":
|
||||||
if (self.left instanceof AST_UnaryPrefix
|
if (self.left instanceof AST_String
|
||||||
&& self.left.operator == "typeof"
|
&& self.left.value == "undefined"
|
||||||
&& self.right instanceof AST_String
|
&& self.right instanceof AST_UnaryPrefix
|
||||||
&& self.right.value == "undefined") {
|
&& self.right.operator == "typeof") {
|
||||||
if (!(self.left.expression instanceof AST_SymbolRef)
|
|
||||||
|| !self.left.expression.undeclared()) {
|
|
||||||
self.left = self.left.expression;
|
|
||||||
self.right = make_node(AST_Undefined, self.right).optimize(compressor);
|
|
||||||
if (self.operator.length == 2) self.operator += "=";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (self.left instanceof AST_String
|
|
||||||
&& self.left.value == "undefined"
|
|
||||||
&& self.right instanceof AST_UnaryPrefix
|
|
||||||
&& self.right.operator == "typeof") {
|
|
||||||
if (!(self.right.expression instanceof AST_SymbolRef)
|
if (!(self.right.expression instanceof AST_SymbolRef)
|
||||||
|| !self.right.expression.undeclared()) {
|
|| !self.right.expression.undeclared()) {
|
||||||
self.left = self.right.expression;
|
self.left = self.right.expression;
|
||||||
@@ -1468,12 +1625,6 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
self = best_of(self, negated);
|
self = best_of(self, negated);
|
||||||
}
|
}
|
||||||
var reverse = function(op) {
|
|
||||||
self.operator = op;
|
|
||||||
var tmp = self.left;
|
|
||||||
self.left = self.right;
|
|
||||||
self.right = tmp;
|
|
||||||
};
|
|
||||||
switch (self.operator) {
|
switch (self.operator) {
|
||||||
case "<": reverse(">"); break;
|
case "<": reverse(">"); break;
|
||||||
case "<=": reverse(">="); break;
|
case "<=": reverse(">="); break;
|
||||||
@@ -1519,6 +1670,7 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
|
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
|
||||||
OPT(AST_Assign, function(self, compressor){
|
OPT(AST_Assign, function(self, compressor){
|
||||||
|
self = self.lift_sequences(compressor);
|
||||||
if (self.operator == "="
|
if (self.operator == "="
|
||||||
&& self.left instanceof AST_SymbolRef
|
&& self.left instanceof AST_SymbolRef
|
||||||
&& self.right instanceof AST_Binary
|
&& self.right instanceof AST_Binary
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ function OutputStream(options) {
|
|||||||
str = String(str);
|
str = String(str);
|
||||||
var ch = str.charAt(0);
|
var ch = str.charAt(0);
|
||||||
if (might_need_semicolon) {
|
if (might_need_semicolon) {
|
||||||
if (";}".indexOf(ch) < 0 && !/[;]$/.test(last)) {
|
if ((!ch || ";}".indexOf(ch) < 0) && !/[;]$/.test(last)) {
|
||||||
if (options.semicolons || requireSemicolonChars(ch)) {
|
if (options.semicolons || requireSemicolonChars(ch)) {
|
||||||
OUTPUT += ";";
|
OUTPUT += ";";
|
||||||
current_col++;
|
current_col++;
|
||||||
@@ -410,11 +410,16 @@ function OutputStream(options) {
|
|||||||
return first_in_statement(output);
|
return first_in_statement(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS(AST_Unary, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
return p instanceof AST_PropAccess && p.expression === this;
|
||||||
|
});
|
||||||
|
|
||||||
PARENS(AST_Seq, function(output){
|
PARENS(AST_Seq, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
return p instanceof AST_Call // (foo, bar)() or foo(1, (2, 3), 4)
|
||||||
|| p instanceof AST_Unary // !(foo, bar, baz)
|
|| p instanceof AST_Unary // !(foo, bar, baz)
|
||||||
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 7
|
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|
||||||
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
||||||
|| p instanceof AST_Dot // (1, {foo:2}).foo ==> 2
|
|| p instanceof AST_Dot // (1, {foo:2}).foo ==> 2
|
||||||
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||||
@@ -463,10 +468,36 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
PARENS(AST_PropAccess, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
if (p instanceof AST_New && p.expression === this) {
|
||||||
|
// i.e. new (foo.bar().baz)
|
||||||
|
//
|
||||||
|
// if there's one call into this subtree, then we need
|
||||||
|
// parens around it too, otherwise the call will be
|
||||||
|
// interpreted as passing the arguments to the upper New
|
||||||
|
// expression.
|
||||||
|
try {
|
||||||
|
this.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Call) throw p;
|
||||||
|
}));
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== p) throw ex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PARENS(AST_Call, function(output){
|
||||||
|
var p = output.parent();
|
||||||
|
return p instanceof AST_New && p.expression === this;
|
||||||
|
});
|
||||||
|
|
||||||
PARENS(AST_New, function(output){
|
PARENS(AST_New, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// (new Date).getTime();
|
if (no_constructor_parens(this, output)
|
||||||
if (p instanceof AST_Dot && no_constructor_parens(this, output))
|
&& (p instanceof AST_Dot // (new Date).getTime()
|
||||||
|
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -529,6 +560,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
DEFPRINT(AST_Toplevel, function(self, output){
|
DEFPRINT(AST_Toplevel, function(self, output){
|
||||||
display_body(self.body, true, output);
|
display_body(self.body, true, output);
|
||||||
|
output.print("");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_LabeledStatement, function(self, output){
|
DEFPRINT(AST_LabeledStatement, function(self, output){
|
||||||
self.label.print(output);
|
self.label.print(output);
|
||||||
|
|||||||
@@ -883,6 +883,8 @@ function parse($TEXT, options) {
|
|||||||
var function_ = function(in_statement, ctor) {
|
var function_ = function(in_statement, ctor) {
|
||||||
var name = is("name") ? as_symbol(in_statement
|
var name = is("name") ? as_symbol(in_statement
|
||||||
? AST_SymbolDefun
|
? AST_SymbolDefun
|
||||||
|
: ctor === AST_Accessor
|
||||||
|
? AST_SymbolAccessor
|
||||||
: AST_SymbolLambda) : null;
|
: AST_SymbolLambda) : null;
|
||||||
if (in_statement && !name)
|
if (in_statement && !name)
|
||||||
unexpected();
|
unexpected();
|
||||||
@@ -1158,7 +1160,7 @@ function parse($TEXT, options) {
|
|||||||
a.push(new AST_ObjectGetter({
|
a.push(new AST_ObjectGetter({
|
||||||
start : start,
|
start : start,
|
||||||
key : name,
|
key : name,
|
||||||
value : function_(false, AST_Lambda),
|
value : function_(false, AST_Accessor),
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
@@ -1167,7 +1169,7 @@ function parse($TEXT, options) {
|
|||||||
a.push(new AST_ObjectSetter({
|
a.push(new AST_ObjectSetter({
|
||||||
start : start,
|
start : start,
|
||||||
key : name,
|
key : name,
|
||||||
value : function_(false, AST_Lambda),
|
value : function_(false, AST_Accessor),
|
||||||
end : prev()
|
end : prev()
|
||||||
}));
|
}));
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
113
lib/scope.js
113
lib/scope.js
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function SymbolDef(scope, orig) {
|
function SymbolDef(scope, index, orig) {
|
||||||
this.name = orig.name;
|
this.name = orig.name;
|
||||||
this.orig = [ orig ];
|
this.orig = [ orig ];
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
@@ -52,15 +52,18 @@ function SymbolDef(scope, orig) {
|
|||||||
this.mangled_name = null;
|
this.mangled_name = null;
|
||||||
this.undeclared = false;
|
this.undeclared = false;
|
||||||
this.constant = false;
|
this.constant = false;
|
||||||
|
this.index = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
SymbolDef.prototype = {
|
SymbolDef.prototype = {
|
||||||
unmangleable: function() {
|
unmangleable: function(options) {
|
||||||
return this.global || this.undeclared || this.scope.uses_eval || this.scope.uses_with;
|
return this.global
|
||||||
|
|| this.undeclared
|
||||||
|
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
||||||
},
|
},
|
||||||
mangle: function() {
|
mangle: function(options) {
|
||||||
if (!this.mangled_name && !this.unmangleable())
|
if (!this.mangled_name && !this.unmangleable(options))
|
||||||
this.mangled_name = this.scope.next_mangled();
|
this.mangled_name = this.scope.next_mangled(options);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,14 +78,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var self = this;
|
var self = this;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
var labels = Object.create(null);
|
var labels = new Dictionary();
|
||||||
|
var nesting = 0;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
node.init_scope_vars();
|
node.init_scope_vars(nesting);
|
||||||
var save_scope = node.parent_scope = scope;
|
var save_scope = node.parent_scope = scope;
|
||||||
|
++nesting;
|
||||||
scope = node;
|
scope = node;
|
||||||
descend();
|
descend();
|
||||||
scope = save_scope;
|
scope = save_scope;
|
||||||
|
--nesting;
|
||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Directive) {
|
if (node instanceof AST_Directive) {
|
||||||
@@ -97,11 +103,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_LabeledStatement) {
|
if (node instanceof AST_LabeledStatement) {
|
||||||
var l = node.label;
|
var l = node.label;
|
||||||
if (labels[l.name])
|
if (labels.has(l.name))
|
||||||
throw new Error(string_template("Label {name} defined twice", l));
|
throw new Error(string_template("Label {name} defined twice", l));
|
||||||
labels[l.name] = l;
|
labels.set(l.name, l);
|
||||||
descend();
|
descend();
|
||||||
delete labels[l.name];
|
labels.del(l.name);
|
||||||
return true; // no descend again
|
return true; // no descend again
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolDeclaration) {
|
if (node instanceof AST_SymbolDeclaration) {
|
||||||
@@ -115,7 +121,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
node.init_scope_vars();
|
node.init_scope_vars();
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolLambda) {
|
if (node instanceof AST_SymbolLambda) {
|
||||||
scope.def_function(node);
|
//scope.def_function(node);
|
||||||
|
//
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/24 — MSIE
|
||||||
|
// leaks function expression names into the containing
|
||||||
|
// 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());
|
node.init.push(tw.parent());
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
@@ -144,7 +157,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
scope.def_variable(node);
|
scope.def_variable(node);
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabelRef) {
|
if (node instanceof AST_LabelRef) {
|
||||||
var sym = labels[node.name];
|
var sym = labels.get(node.name);
|
||||||
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
||||||
name: node.name,
|
name: node.name,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
@@ -157,7 +170,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
|
|
||||||
// pass 2: find back references and eval
|
// pass 2: find back references and eval
|
||||||
var func = null;
|
var func = null;
|
||||||
var globals = self.globals = Object.create(null);
|
var globals = self.globals = new Dictionary();
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda) {
|
||||||
var prev_func = func;
|
var prev_func = func;
|
||||||
@@ -175,12 +188,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
var sym = node.scope.find_variable(name);
|
var sym = node.scope.find_variable(name);
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
var g;
|
var g;
|
||||||
if (globals[name]) {
|
if (globals.has(name)) {
|
||||||
g = globals[name];
|
g = globals.get(name);
|
||||||
} else {
|
} else {
|
||||||
g = new SymbolDef(self, node);
|
g = new SymbolDef(self, globals.size(), node);
|
||||||
g.undeclared = true;
|
g.undeclared = true;
|
||||||
globals[name] = g;
|
globals.set(name, g);
|
||||||
}
|
}
|
||||||
node.thedef = g;
|
node.thedef = g;
|
||||||
if (name == "eval" && tw.parent() instanceof AST_Call) {
|
if (name == "eval" && tw.parent() instanceof AST_Call) {
|
||||||
@@ -200,15 +213,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("init_scope_vars", function(){
|
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||||
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
|
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
|
||||||
this.variables = Object.create(null); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||||
this.functions = Object.create(null); // map name to AST_SymbolDefun (functions defined in this scope)
|
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||||
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||||
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
||||||
this.parent_scope = null; // the parent scope
|
this.parent_scope = null; // the parent scope
|
||||||
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
||||||
this.cname = -1; // the current index for mangling functions/variables
|
this.cname = -1; // the current index for mangling functions/variables
|
||||||
|
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("strict", function(){
|
AST_Scope.DEFMETHOD("strict", function(){
|
||||||
@@ -216,7 +230,7 @@ AST_Scope.DEFMETHOD("strict", function(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||||
AST_Scope.prototype.init_scope_vars.call(this);
|
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -229,6 +243,7 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
if (s === def.scope) break;
|
if (s === def.scope) break;
|
||||||
s = s.parent_scope;
|
s = s.parent_scope;
|
||||||
}
|
}
|
||||||
|
this.frame = this.scope.nesting - def.scope.nesting;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
||||||
@@ -245,7 +260,7 @@ AST_LabelRef.DEFMETHOD("reference", function(){
|
|||||||
|
|
||||||
AST_Scope.DEFMETHOD("find_variable", function(name){
|
AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||||
if (name instanceof AST_Symbol) name = name.name;
|
if (name instanceof AST_Symbol) name = name.name;
|
||||||
return this.variables[name]
|
return this.variables.get(name)
|
||||||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -255,23 +270,23 @@ AST_Scope.DEFMETHOD("has_directive", function(value){
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
||||||
this.functions[symbol.name] = this.def_variable(symbol);
|
this.functions.set(symbol.name, this.def_variable(symbol));
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
AST_Scope.DEFMETHOD("def_variable", function(symbol){
|
||||||
var def;
|
var def;
|
||||||
if (!this.variables[symbol.name]) {
|
if (!this.variables.has(symbol.name)) {
|
||||||
def = new SymbolDef(this, symbol);
|
def = new SymbolDef(this, this.variables.size(), symbol);
|
||||||
this.variables[symbol.name] = def;
|
this.variables.set(symbol.name, def);
|
||||||
def.global = !this.parent_scope;
|
def.global = !this.parent_scope;
|
||||||
} else {
|
} else {
|
||||||
def = this.variables[symbol.name];
|
def = this.variables.get(symbol.name);
|
||||||
def.orig.push(symbol);
|
def.orig.push(symbol);
|
||||||
}
|
}
|
||||||
return symbol.thedef = def;
|
return symbol.thedef = def;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("next_mangled", function(){
|
AST_Scope.DEFMETHOD("next_mangled", function(options){
|
||||||
var ext = this.enclosed, n = ext.length;
|
var ext = this.enclosed, n = ext.length;
|
||||||
out: while (true) {
|
out: while (true) {
|
||||||
var m = base54(++this.cname);
|
var m = base54(++this.cname);
|
||||||
@@ -281,7 +296,7 @@ AST_Scope.DEFMETHOD("next_mangled", function(){
|
|||||||
// inner scopes.
|
// inner scopes.
|
||||||
for (var i = n; --i >= 0;) {
|
for (var i = n; --i >= 0;) {
|
||||||
var sym = ext[i];
|
var sym = ext[i];
|
||||||
var name = sym.mangled_name || (sym.unmangleable() && sym.name);
|
var name = sym.mangled_name || (sym.unmangleable(options) && sym.name);
|
||||||
if (m == name) continue out;
|
if (m == name) continue out;
|
||||||
}
|
}
|
||||||
return m;
|
return m;
|
||||||
@@ -293,8 +308,13 @@ AST_Scope.DEFMETHOD("references", function(sym){
|
|||||||
return this.enclosed.indexOf(sym) < 0 ? null : sym;
|
return this.enclosed.indexOf(sym) < 0 ? null : sym;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Symbol.DEFMETHOD("unmangleable", function(){
|
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||||
return this.definition().unmangleable();
|
return this.definition().unmangleable(options);
|
||||||
|
});
|
||||||
|
|
||||||
|
// property accessors are not mangleable
|
||||||
|
AST_SymbolAccessor.DEFMETHOD("unmangleable", function(){
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// labels are always mangleable
|
// labels are always mangleable
|
||||||
@@ -327,10 +347,15 @@ AST_Symbol.DEFMETHOD("global", function(){
|
|||||||
return this.definition().global;
|
return this.definition().global;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||||
options = defaults(options, {
|
return defaults(options, {
|
||||||
except : []
|
except : [],
|
||||||
|
eval : false,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||||
|
options = this._default_mangler_options(options);
|
||||||
// We only need to mangle declaration nodes. Special logic wired
|
// We only need to mangle declaration nodes. Special logic wired
|
||||||
// into the code generator will display the mangled name if it's
|
// into the code generator will display the mangled name if it's
|
||||||
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||||
@@ -347,16 +372,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
var p = tw.parent();
|
var p = tw.parent();
|
||||||
var is_setget = p instanceof AST_ObjectSetter || p instanceof AST_ObjectGetter;
|
node.variables.each(function(symbol){
|
||||||
var a = node.variables;
|
if (options.except.indexOf(symbol.name) < 0) {
|
||||||
for (var i in a) {
|
to_mangle.push(symbol);
|
||||||
var symbol = a[i];
|
|
||||||
if (!(is_setget && symbol instanceof AST_SymbolLambda)) {
|
|
||||||
if (options.except.indexOf(symbol.name) < 0) {
|
|
||||||
to_mangle.push(symbol);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Label) {
|
if (node instanceof AST_Label) {
|
||||||
@@ -370,7 +390,8 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||||
|
options = this._default_mangler_options(options);
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_Constant)
|
if (node instanceof AST_Constant)
|
||||||
base54.consider(node.print_to_string());
|
base54.consider(node.print_to_string());
|
||||||
@@ -428,7 +449,7 @@ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){
|
|||||||
base54.consider("catch");
|
base54.consider("catch");
|
||||||
else if (node instanceof AST_Finally)
|
else if (node instanceof AST_Finally)
|
||||||
base54.consider("finally");
|
base54.consider("finally");
|
||||||
else if (node instanceof AST_Symbol && node.unmangleable())
|
else if (node instanceof AST_Symbol && node.unmangleable(options))
|
||||||
base54.consider(node.name);
|
base54.consider(node.name);
|
||||||
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
else if (node instanceof AST_Unary || node instanceof AST_Binary)
|
||||||
base54.consider(node.operator);
|
base54.consider(node.operator);
|
||||||
|
|||||||
40
lib/utils.js
40
lib/utils.js
@@ -166,6 +166,12 @@ function string_template(text, props) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function remove(array, el) {
|
||||||
|
for (var i = array.length; --i >= 0;) {
|
||||||
|
if (array[i] === el) array.splice(i, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function mergeSort(array, cmp) {
|
function mergeSort(array, cmp) {
|
||||||
if (array.length < 2) return array.slice();
|
if (array.length < 2) return array.slice();
|
||||||
function merge(a, b) {
|
function merge(a, b) {
|
||||||
@@ -238,3 +244,37 @@ function makePredicate(words) {
|
|||||||
}
|
}
|
||||||
return new Function("str", f);
|
return new Function("str", f);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function Dictionary() {
|
||||||
|
this._values = Object.create(null);
|
||||||
|
this._size = 0;
|
||||||
|
};
|
||||||
|
Dictionary.prototype = {
|
||||||
|
set: function(key, val) {
|
||||||
|
if (!this.has(key)) ++this._size;
|
||||||
|
this._values["$" + key] = val;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
get: function(key) { return this._values["$" + key] },
|
||||||
|
del: function(key) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
--this._size;
|
||||||
|
delete this._values["$" + key];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
has: function(key) { return ("$" + key) in this._values },
|
||||||
|
each: function(f) {
|
||||||
|
for (var i in this._values)
|
||||||
|
f(this._values[i], i.substr(1));
|
||||||
|
},
|
||||||
|
size: function() {
|
||||||
|
return this._size;
|
||||||
|
},
|
||||||
|
map: function(f) {
|
||||||
|
var ret = [];
|
||||||
|
for (var i in this._values)
|
||||||
|
ret.push(f(this._values[i], i.substr(1)));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"main": "tools/node.js",
|
"main": "tools/node.js",
|
||||||
"version": "2.1.1",
|
"version": "2.1.10",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
|
|||||||
17
test/compress/issue-22.js
Normal file
17
test/compress/issue-22.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
return_with_no_value_in_if_body: {
|
||||||
|
options = { conditionals: true };
|
||||||
|
input: {
|
||||||
|
function foo(bar) {
|
||||||
|
if (bar) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo (bar) {
|
||||||
|
return bar ? void 0 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
test/compress/labels.js
Normal file
163
test/compress/labels.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
labels_1: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: {
|
||||||
|
if (foo) break out;
|
||||||
|
console.log("bar");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
foo || console.log("bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_2: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: {
|
||||||
|
if (foo) print("stuff");
|
||||||
|
else break out;
|
||||||
|
console.log("here");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
if (foo) {
|
||||||
|
print("stuff");
|
||||||
|
console.log("here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_3: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
for (var i = 0; i < 5; ++i) {
|
||||||
|
if (i < 3) continue;
|
||||||
|
console.log(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
for (var i = 0; i < 5; ++i)
|
||||||
|
i < 3 || console.log(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_4: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: for (var i = 0; i < 5; ++i) {
|
||||||
|
if (i < 3) continue out;
|
||||||
|
console.log(i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
for (var i = 0; i < 5; ++i)
|
||||||
|
i < 3 || console.log(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_5: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
// should keep the break-s in the following
|
||||||
|
input: {
|
||||||
|
while (foo) {
|
||||||
|
if (bar) break;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
out: while (foo) {
|
||||||
|
if (bar) break out;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
if (bar) break;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
out: while (foo) {
|
||||||
|
if (bar) break out;
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_6: {
|
||||||
|
input: {
|
||||||
|
out: break out;
|
||||||
|
};
|
||||||
|
expect: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_7: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_8: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_9: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
continue out;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels_10: {
|
||||||
|
options = { if_return: true, conditionals: true, dead_code: true };
|
||||||
|
input: {
|
||||||
|
out: while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break out;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect: {
|
||||||
|
out: while (foo) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
break out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
test/compress/loops.js
Normal file
123
test/compress/loops.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
while_becomes_for: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
while (foo()) bar();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; foo(); ) bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_1: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;;)
|
||||||
|
if (foo()) break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; !foo(););
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_2: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();)
|
||||||
|
if (foo()) break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && !foo(););
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_3: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) break;
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && !foo();) {
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_4: {
|
||||||
|
options = { loops: true, sequences: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
if (foo()) break;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && (x(), y(), !foo());) z(), k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_1: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;;) if (foo()) bar(); else break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; foo(); ) bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_2: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && foo();) baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_3: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && foo();) {
|
||||||
|
baz();
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_4: {
|
||||||
|
options = { loops: true, sequences: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && (x(), y(), foo());) baz(), z(), k();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -87,3 +87,75 @@ make_sequences_4: {
|
|||||||
with (x = 5, obj);
|
with (x = 5, obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lift_sequences_1: {
|
||||||
|
options = { sequences: true };
|
||||||
|
input: {
|
||||||
|
foo = !(x(), y(), bar());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x(), y(), foo = !bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lift_sequences_2: {
|
||||||
|
options = { sequences: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
q = 1 + (foo(), bar(), 5) + 7 * (5 / (3 - (a(), (QW=ER), c(), 2))) - (x(), y(), 5);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo(), bar(), a(), QW = ER, c(), x(), y(), q = 36
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lift_sequences_3: {
|
||||||
|
options = { sequences: true, conditionals: true };
|
||||||
|
input: {
|
||||||
|
x = (foo(), bar(), baz()) ? 10 : 20;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo(), bar(), x = baz() ? 10 : 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lift_sequences_4: {
|
||||||
|
options = { side_effects: true };
|
||||||
|
input: {
|
||||||
|
x = (foo, bar, baz);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x = baz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for_sequences: {
|
||||||
|
options = { sequences: true };
|
||||||
|
input: {
|
||||||
|
// 1
|
||||||
|
foo();
|
||||||
|
bar();
|
||||||
|
for (; false;);
|
||||||
|
// 2
|
||||||
|
foo();
|
||||||
|
bar();
|
||||||
|
for (x = 5; false;);
|
||||||
|
// 3
|
||||||
|
x = (foo in bar);
|
||||||
|
for (; false;);
|
||||||
|
// 4
|
||||||
|
x = (foo in bar);
|
||||||
|
for (y = 5; false;);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
// 1
|
||||||
|
for (foo(), bar(); false;);
|
||||||
|
// 2
|
||||||
|
for (foo(), bar(), x = 5; false;);
|
||||||
|
// 3
|
||||||
|
x = (foo in bar);
|
||||||
|
for (; false;);
|
||||||
|
// 4
|
||||||
|
x = (foo in bar);
|
||||||
|
for (y = 5; false;);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,12 +73,13 @@ function run_compress_tests() {
|
|||||||
var cmp = new U.Compressor(options, true);
|
var cmp = new U.Compressor(options, true);
|
||||||
var expect = make_code(as_toplevel(test.expect), false);
|
var expect = make_code(as_toplevel(test.expect), false);
|
||||||
var input = as_toplevel(test.input);
|
var input = as_toplevel(test.input);
|
||||||
|
var input_code = make_code(test.input);
|
||||||
var output = input.transform(cmp);
|
var output = input.transform(cmp);
|
||||||
output.figure_out_scope();
|
output.figure_out_scope();
|
||||||
output = make_code(output, false);
|
output = make_code(output, false);
|
||||||
if (expect != output) {
|
if (expect != output) {
|
||||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||||
input: make_code(test.input),
|
input: input_code,
|
||||||
output: output,
|
output: output,
|
||||||
expected: expect
|
expected: expect
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,27 +1,14 @@
|
|||||||
var save_stderr = process.stderr;
|
var path = require("path");
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
|
|
||||||
// discard annoying NodeJS warning ("path.existsSync is now called `fs.existsSync`.")
|
|
||||||
var devnull = fs.createWriteStream("/dev/null");
|
|
||||||
process.__defineGetter__("stderr", function(){
|
|
||||||
return devnull;
|
|
||||||
});
|
|
||||||
|
|
||||||
var vm = require("vm");
|
var vm = require("vm");
|
||||||
var sys = require("util");
|
var sys = require("util");
|
||||||
var path = require("path");
|
|
||||||
|
|
||||||
var UglifyJS = vm.createContext({
|
var UglifyJS = vm.createContext({
|
||||||
sys : sys,
|
sys : sys,
|
||||||
console : console,
|
console : console,
|
||||||
|
|
||||||
MOZ_SourceMap : require("source-map")
|
MOZ_SourceMap : require("source-map")
|
||||||
});
|
});
|
||||||
|
|
||||||
process.__defineGetter__("stderr", function(){
|
|
||||||
return save_stderr;
|
|
||||||
});
|
|
||||||
|
|
||||||
function load_global(file) {
|
function load_global(file) {
|
||||||
file = path.resolve(path.dirname(module.filename), file);
|
file = path.resolve(path.dirname(module.filename), file);
|
||||||
try {
|
try {
|
||||||
@@ -65,7 +52,9 @@ for (var i in UglifyJS) {
|
|||||||
exports.minify = function(files, options) {
|
exports.minify = function(files, options) {
|
||||||
options = UglifyJS.defaults(options, {
|
options = UglifyJS.defaults(options, {
|
||||||
outSourceMap : null,
|
outSourceMap : null,
|
||||||
|
sourceRoot : null,
|
||||||
inSourceMap : null,
|
inSourceMap : null,
|
||||||
|
fromString : false,
|
||||||
warnings : false,
|
warnings : false,
|
||||||
});
|
});
|
||||||
if (typeof files == "string")
|
if (typeof files == "string")
|
||||||
@@ -74,9 +63,11 @@ exports.minify = function(files, options) {
|
|||||||
// 1. parse
|
// 1. parse
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
var code = fs.readFileSync(file, "utf8");
|
var code = options.fromString
|
||||||
|
? file
|
||||||
|
: fs.readFileSync(file, "utf8");
|
||||||
toplevel = UglifyJS.parse(code, {
|
toplevel = UglifyJS.parse(code, {
|
||||||
filename: file,
|
filename: options.fromString ? "?" : file,
|
||||||
toplevel: toplevel
|
toplevel: toplevel
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -101,7 +92,8 @@ exports.minify = function(files, options) {
|
|||||||
}
|
}
|
||||||
if (options.outSourceMap) map = UglifyJS.SourceMap({
|
if (options.outSourceMap) map = UglifyJS.SourceMap({
|
||||||
file: options.outSourceMap,
|
file: options.outSourceMap,
|
||||||
orig: inMap
|
orig: inMap,
|
||||||
|
root: options.sourceRoot
|
||||||
});
|
});
|
||||||
var stream = UglifyJS.OutputStream({ source_map: map });
|
var stream = UglifyJS.OutputStream({ source_map: map });
|
||||||
toplevel.print(stream);
|
toplevel.print(stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user