Merge branch 'master' into harmony
This commit is contained in:
@@ -348,7 +348,7 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
||||
- `drop_console` -- default `false`. Pass `true` to discard calls to
|
||||
`console.*` functions.
|
||||
|
||||
- `keep_fargs` -- default `false`. Pass `true` to prevent the
|
||||
- `keep_fargs` -- default `true`. Prevents the
|
||||
compressor from discarding unused function arguments. You need this
|
||||
for code which relies on `Function.length`.
|
||||
|
||||
@@ -372,7 +372,6 @@ when this flag is on:
|
||||
- `void 0` → `undefined` (if there is a variable named "undefined" in
|
||||
scope; we do it because the variable name will be mangled, typically
|
||||
reduced to a single character)
|
||||
- discards unused function arguments (affects `function.length`)
|
||||
|
||||
### Conditional compilation
|
||||
|
||||
@@ -784,7 +783,7 @@ The `source_map_options` (optional) can contain the following properties:
|
||||
came from. It can be simply a string in JSON, or a JSON object containing
|
||||
the original source map.
|
||||
|
||||
[acorn]: https://github.com/marijnh/acorn
|
||||
[acorn]: https://github.com/ternjs/acorn
|
||||
[source-map]: https://github.com/mozilla/source-map
|
||||
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
|
||||
[codegen]: http://lisperator.net/uglifyjs/codegen
|
||||
|
||||
73
bin/uglifyjs
73
bin/uglifyjs
@@ -73,6 +73,7 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.describe("mangle-regex", "Only mangle property names matching the regex")
|
||||
.describe("name-cache", "File to hold mangled names mappings")
|
||||
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
|
||||
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
|
||||
|
||||
.alias("p", "prefix")
|
||||
.alias("o", "output")
|
||||
@@ -117,6 +118,7 @@ You need to pass an argument to this option to specify the name that your module
|
||||
.boolean("stats")
|
||||
.boolean("acorn")
|
||||
.boolean("spidermonkey")
|
||||
.boolean("dump-spidermonkey-ast")
|
||||
.boolean("lint")
|
||||
.boolean("V")
|
||||
.boolean("version")
|
||||
@@ -407,14 +409,17 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
writeNameCache("props", cache);
|
||||
})();
|
||||
|
||||
var SCOPE_IS_NEEDED = COMPRESS || MANGLE || ARGS.lint
|
||||
var TL_CACHE = readNameCache("vars");
|
||||
|
||||
time_it("scope", function(){
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||
if (ARGS.lint) {
|
||||
TOPLEVEL.scope_warnings();
|
||||
}
|
||||
});
|
||||
if (SCOPE_IS_NEEDED) {
|
||||
time_it("scope", function(){
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||
if (ARGS.lint) {
|
||||
TOPLEVEL.scope_warnings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (COMPRESS) {
|
||||
time_it("squeeze", function(){
|
||||
@@ -422,12 +427,14 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
});
|
||||
}
|
||||
|
||||
time_it("scope", function(){
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||
if (MANGLE && !TL_CACHE) {
|
||||
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||
}
|
||||
});
|
||||
if (SCOPE_IS_NEEDED) {
|
||||
time_it("scope", function(){
|
||||
TOPLEVEL.figure_out_scope({ screw_ie8: ARGS.screw_ie8, cache: TL_CACHE });
|
||||
if (MANGLE && !TL_CACHE) {
|
||||
TOPLEVEL.compute_char_frequency(MANGLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (MANGLE) time_it("mangle", function(){
|
||||
MANGLE.cache = TL_CACHE;
|
||||
@@ -444,26 +451,30 @@ async.eachLimit(files, 1, function (file, cb) {
|
||||
}
|
||||
}
|
||||
|
||||
time_it("generate", function(){
|
||||
TOPLEVEL.print(output);
|
||||
});
|
||||
|
||||
output = output.get();
|
||||
|
||||
if (SOURCE_MAP) {
|
||||
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
||||
var source_map_url = ARGS.source_map_url || (
|
||||
P_RELATIVE
|
||||
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
|
||||
: ARGS.source_map
|
||||
);
|
||||
output += "\n//# sourceMappingURL=" + source_map_url;
|
||||
}
|
||||
|
||||
if (OUTPUT_FILE) {
|
||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||
if (ARGS.dump_spidermonkey_ast) {
|
||||
print(JSON.stringify(TOPLEVEL.to_mozilla_ast(), null, 2));
|
||||
} else {
|
||||
print(output);
|
||||
time_it("generate", function(){
|
||||
TOPLEVEL.print(output);
|
||||
});
|
||||
|
||||
output = output.get();
|
||||
|
||||
if (SOURCE_MAP) {
|
||||
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
|
||||
var source_map_url = ARGS.source_map_url || (
|
||||
P_RELATIVE
|
||||
? path.relative(path.dirname(OUTPUT_FILE), ARGS.source_map)
|
||||
: ARGS.source_map
|
||||
);
|
||||
output += "\n//# sourceMappingURL=" + source_map_url;
|
||||
}
|
||||
|
||||
if (OUTPUT_FILE) {
|
||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||
} else {
|
||||
print(output);
|
||||
}
|
||||
}
|
||||
|
||||
if (ARGS.stats) {
|
||||
|
||||
30
lib/ast.js
30
lib/ast.js
@@ -85,7 +85,7 @@ function DEFNODE(type, props, methods, base) {
|
||||
return ctor;
|
||||
};
|
||||
|
||||
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", {
|
||||
var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file raw", {
|
||||
}, null);
|
||||
|
||||
var AST_Node = DEFNODE("Node", "start end", {
|
||||
@@ -1159,27 +1159,36 @@ var AST_True = DEFNODE("True", null, {
|
||||
function TreeWalker(callback) {
|
||||
this.visit = callback;
|
||||
this.stack = [];
|
||||
this.directives = Object.create(null);
|
||||
};
|
||||
TreeWalker.prototype = {
|
||||
_visit: function(node, descend) {
|
||||
this.stack.push(node);
|
||||
this.push(node);
|
||||
var ret = this.visit(node, descend ? function(){
|
||||
descend.call(node);
|
||||
} : noop);
|
||||
if (!ret && descend) {
|
||||
descend.call(node);
|
||||
}
|
||||
this.stack.pop();
|
||||
this.pop(node);
|
||||
return ret;
|
||||
},
|
||||
parent: function(n) {
|
||||
return this.stack[this.stack.length - 2 - (n || 0)];
|
||||
},
|
||||
push: function (node) {
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.create(this.directives);
|
||||
} else if (node instanceof AST_Directive) {
|
||||
this.directives[node.value] = this.directives[node.value] ? "up" : true;
|
||||
}
|
||||
this.stack.push(node);
|
||||
},
|
||||
pop: function() {
|
||||
return this.stack.pop();
|
||||
pop: function(node) {
|
||||
this.stack.pop();
|
||||
if (node instanceof AST_Lambda) {
|
||||
this.directives = Object.getPrototypeOf(this.directives);
|
||||
}
|
||||
},
|
||||
self: function() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
@@ -1192,7 +1201,16 @@ TreeWalker.prototype = {
|
||||
}
|
||||
},
|
||||
has_directive: function(type) {
|
||||
return this.find_parent(AST_Scope).has_directive(type);
|
||||
var dir = this.directives[type];
|
||||
if (dir) return dir;
|
||||
var node = this.stack[this.stack.length - 1];
|
||||
if (node instanceof AST_Scope && node.body) {
|
||||
for (var i = 0; i < node.body.length; ++i) {
|
||||
var st = node.body[i];
|
||||
if (!(st instanceof AST_Directive)) break;
|
||||
if (st.value == type) return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
in_boolean_context: function() {
|
||||
var stack = this.stack;
|
||||
|
||||
@@ -199,7 +199,7 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
|
||||
function tighten_body(statements, compressor) {
|
||||
var CHANGED;
|
||||
var CHANGED, max_iter = 10;
|
||||
do {
|
||||
CHANGED = false;
|
||||
if (compressor.option("angular")) {
|
||||
@@ -218,7 +218,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("join_vars")) {
|
||||
statements = join_consecutive_vars(statements, compressor);
|
||||
}
|
||||
} while (CHANGED);
|
||||
} while (CHANGED && max_iter-- > 0);
|
||||
|
||||
if (compressor.option("negate_iife")) {
|
||||
negate_iifes(statements, compressor);
|
||||
@@ -392,7 +392,12 @@ merge(Compressor.prototype, {
|
||||
continue loop;
|
||||
}
|
||||
//---
|
||||
if (ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
|
||||
// XXX: what was the intention of this case?
|
||||
// if sequences is not enabled, this can lead to an endless loop (issue #866).
|
||||
// however, with sequences on this helps producing slightly better output for
|
||||
// the example code.
|
||||
if (compressor.option("sequences")
|
||||
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
|
||||
&& (!stat.alternative || stat.alternative instanceof AST_SimpleStatement)) {
|
||||
CHANGED = true;
|
||||
ret.push(make_node(AST_Return, ret[0], {
|
||||
@@ -729,6 +734,32 @@ merge(Compressor.prototype, {
|
||||
return [ this ];
|
||||
}
|
||||
});
|
||||
AST_Node.DEFMETHOD("is_constant", function(compressor){
|
||||
// Accomodate when compress option evaluate=false
|
||||
// as well as the common constant expressions !0 and !1
|
||||
return this instanceof AST_Constant
|
||||
|| (this instanceof AST_UnaryPrefix && this.operator == "!"
|
||||
&& this.expression instanceof AST_Constant)
|
||||
|| this.evaluate(compressor).length > 1;
|
||||
});
|
||||
// Obtain the constant value of an expression already known to be constant.
|
||||
// Result only valid iff this.is_constant(compressor) is true.
|
||||
AST_Node.DEFMETHOD("constant_value", function(compressor){
|
||||
// Accomodate when option evaluate=false.
|
||||
if (this instanceof AST_Constant) return this.value;
|
||||
// Accomodate the common constant expressions !0 and !1 when option evaluate=false.
|
||||
if (this instanceof AST_UnaryPrefix
|
||||
&& this.operator == "!"
|
||||
&& this.expression instanceof AST_Constant) {
|
||||
return !this.expression.value;
|
||||
}
|
||||
var result = this.evaluate(compressor)
|
||||
if (result.length > 1) {
|
||||
return result[1];
|
||||
}
|
||||
// should never be reached
|
||||
return undefined;
|
||||
});
|
||||
def(AST_Statement, function(){
|
||||
throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
|
||||
});
|
||||
@@ -1006,7 +1037,7 @@ merge(Compressor.prototype, {
|
||||
/* -----[ optimizers ]----- */
|
||||
|
||||
OPT(AST_Directive, function(self, compressor){
|
||||
if (self.scope.has_directive(self.value) !== self.scope) {
|
||||
if (compressor.has_directive(self.value) === "up") {
|
||||
return make_node(AST_EmptyStatement, self);
|
||||
}
|
||||
return self;
|
||||
@@ -2472,32 +2503,48 @@ merge(Compressor.prototype, {
|
||||
alternative: alternative
|
||||
});
|
||||
}
|
||||
// x=y?1:1 --> x=1
|
||||
if (consequent instanceof AST_Constant
|
||||
&& alternative instanceof AST_Constant
|
||||
// y?1:1 --> 1
|
||||
if (consequent.is_constant(compressor)
|
||||
&& alternative.is_constant(compressor)
|
||||
&& consequent.equivalent_to(alternative)) {
|
||||
var consequent_value = consequent.constant_value();
|
||||
if (self.condition.has_side_effects(compressor)) {
|
||||
return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent.value, self)]);
|
||||
return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent_value, self)]);
|
||||
} else {
|
||||
return make_node_from_constant(compressor, consequent.value, self);
|
||||
|
||||
return make_node_from_constant(compressor, consequent_value, self);
|
||||
}
|
||||
}
|
||||
// x=y?true:false --> x=!!y
|
||||
if (consequent instanceof AST_True
|
||||
&& alternative instanceof AST_False) {
|
||||
|
||||
// y?true:false --> !!y
|
||||
if (is_true(consequent) && is_false(alternative)) {
|
||||
self.condition = self.condition.negate(compressor);
|
||||
return make_node(AST_UnaryPrefix, self.condition, {
|
||||
operator: "!",
|
||||
expression: self.condition
|
||||
});
|
||||
}
|
||||
// x=y?false:true --> x=!y
|
||||
if (consequent instanceof AST_False
|
||||
&& alternative instanceof AST_True) {
|
||||
// y?false:true --> !y
|
||||
if (is_false(consequent) && is_true(alternative)) {
|
||||
return self.condition.negate(compressor)
|
||||
}
|
||||
return self;
|
||||
|
||||
// AST_True or !0
|
||||
function is_true(node) {
|
||||
return node instanceof AST_True
|
||||
|| (node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "!"
|
||||
&& node.expression instanceof AST_Constant
|
||||
&& !node.expression.value);
|
||||
}
|
||||
// AST_False or !1
|
||||
function is_false(node) {
|
||||
return node instanceof AST_False
|
||||
|| (node instanceof AST_UnaryPrefix
|
||||
&& node.operator == "!"
|
||||
&& node.expression instanceof AST_Constant
|
||||
&& !!node.expression.value);
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_Boolean, function(self, compressor){
|
||||
@@ -2569,4 +2616,11 @@ merge(Compressor.prototype, {
|
||||
OPT(AST_Object, literals_in_boolean_context);
|
||||
OPT(AST_RegExp, literals_in_boolean_context);
|
||||
|
||||
OPT(AST_Return, function(self, compressor){
|
||||
if (self.value instanceof AST_Undefined) {
|
||||
self.value = null;
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -146,7 +146,14 @@
|
||||
case "boolean":
|
||||
return new (val ? AST_True : AST_False)(args);
|
||||
default:
|
||||
args.value = val;
|
||||
var rx = M.regex;
|
||||
if (rx && rx.pattern) {
|
||||
// RegExpLiteral as per ESTree AST spec
|
||||
args.value = new RegExp(rx.pattern, rx.flags).toString();
|
||||
} else {
|
||||
// support legacy RegExp
|
||||
args.value = M.regex && M.raw ? M.raw : val;
|
||||
}
|
||||
return new AST_RegExp(args);
|
||||
}
|
||||
},
|
||||
@@ -334,6 +341,19 @@
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_RegExp, function To_Moz_RegExpLiteral(M) {
|
||||
var value = M.value;
|
||||
return {
|
||||
type: "Literal",
|
||||
value: value,
|
||||
raw: value.toString(),
|
||||
regex: {
|
||||
pattern: value.source,
|
||||
flags: value.toString().match(/[gimuy]*$/)[0]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
def_to_moz(AST_Constant, function To_Moz_Literal(M) {
|
||||
var value = M.value;
|
||||
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
|
||||
@@ -343,13 +363,15 @@
|
||||
prefix: true,
|
||||
argument: {
|
||||
type: "Literal",
|
||||
value: -value
|
||||
value: -value,
|
||||
raw: M.start.raw
|
||||
}
|
||||
};
|
||||
}
|
||||
return {
|
||||
type: "Literal",
|
||||
value: value
|
||||
value: value,
|
||||
raw: M.start.raw
|
||||
};
|
||||
});
|
||||
|
||||
@@ -369,6 +391,12 @@
|
||||
|
||||
/* -----[ tools ]----- */
|
||||
|
||||
function raw_token(moznode) {
|
||||
if (moznode.type == "Literal") {
|
||||
return moznode.raw != null ? moznode.raw : moznode.value + "";
|
||||
}
|
||||
}
|
||||
|
||||
function my_start_token(moznode) {
|
||||
var loc = moznode.loc, start = loc && loc.start;
|
||||
var range = moznode.range;
|
||||
@@ -379,7 +407,8 @@
|
||||
pos : range ? range[0] : moznode.start,
|
||||
endline : start && start.line,
|
||||
endcol : start && start.column,
|
||||
endpos : range ? range[0] : moznode.start
|
||||
endpos : range ? range[0] : moznode.start,
|
||||
raw : raw_token(moznode),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -393,7 +422,8 @@
|
||||
pos : range ? range[1] : moznode.end,
|
||||
endline : end && end.line,
|
||||
endcol : end && end.column,
|
||||
endpos : range ? range[1] : moznode.end
|
||||
endpos : range ? range[1] : moznode.end,
|
||||
raw : raw_token(moznode),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ function OutputStream(options) {
|
||||
case "\f": return "\\f";
|
||||
case "\n": return "\\n";
|
||||
case "\r": return "\\r";
|
||||
case "\x0B": return output.option("screw_ie8") ? "\\v" : "\\x0B";
|
||||
case "\x0B": return options.screw_ie8 ? "\\v" : "\\x0B";
|
||||
case "\u2028": return "\\u2028";
|
||||
case "\u2029": return "\\u2029";
|
||||
case '"': ++dq; return '"';
|
||||
@@ -382,8 +382,13 @@ function OutputStream(options) {
|
||||
nodetype.DEFMETHOD("_codegen", generator);
|
||||
};
|
||||
|
||||
var use_asm = false;
|
||||
|
||||
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||
var self = this, generator = self._codegen;
|
||||
var self = this, generator = self._codegen, prev_use_asm = use_asm;
|
||||
if (self instanceof AST_Directive && self.value == "use asm") {
|
||||
use_asm = true;
|
||||
}
|
||||
function doit() {
|
||||
self.add_comments(stream);
|
||||
self.add_source_map(stream);
|
||||
@@ -396,6 +401,9 @@ function OutputStream(options) {
|
||||
doit();
|
||||
}
|
||||
stream.pop_node();
|
||||
if (self instanceof AST_Lambda) {
|
||||
use_asm = prev_use_asm;
|
||||
}
|
||||
});
|
||||
|
||||
AST_Node.DEFMETHOD("print_to_string", function(options){
|
||||
@@ -1311,10 +1319,8 @@ function OutputStream(options) {
|
||||
output.print_string(self.getValue(), self.quote);
|
||||
});
|
||||
DEFPRINT(AST_Number, function(self, output){
|
||||
if (self.literal !== undefined
|
||||
&& +self.literal === self.value /* paranoid check */
|
||||
&& self.scope && self.scope.has_directive('use asm')) {
|
||||
output.print(self.literal);
|
||||
if (use_asm && self.start.raw != null) {
|
||||
output.print(self.start.raw);
|
||||
} else {
|
||||
output.print(make_num(self.getValue()));
|
||||
}
|
||||
|
||||
21
lib/parse.js
21
lib/parse.js
@@ -190,6 +190,9 @@ function parse_js_number(num) {
|
||||
return parseInt(num.substr(2), 2);
|
||||
} else if (RE_DEC_NUMBER.test(num)) {
|
||||
return parseFloat(num);
|
||||
} else {
|
||||
var val = parseFloat(num);
|
||||
if (val == num) return val;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -291,6 +294,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
nlb : S.newline_before,
|
||||
file : filename
|
||||
};
|
||||
if (/^(?:num|string|regexp)$/i.test(type)) {
|
||||
ret.raw = $TEXT.substring(ret.pos, ret.endpos);
|
||||
}
|
||||
if (!is_comment) {
|
||||
ret.comments_before = S.comments_before;
|
||||
S.comments_before = [];
|
||||
@@ -341,11 +347,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
if (prefix) num = prefix + num;
|
||||
var valid = parse_js_number(num);
|
||||
if (!isNaN(valid)) {
|
||||
var tok = token("num", valid);
|
||||
if (num.indexOf('.') >= 0) {
|
||||
tok.literal = num;
|
||||
}
|
||||
return tok;
|
||||
return token("num", valid);
|
||||
} else {
|
||||
parse_error("Invalid syntax: " + num);
|
||||
}
|
||||
@@ -406,6 +408,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
|
||||
else ch = read_escaped_char(true);
|
||||
}
|
||||
else if (ch == "\n") parse_error("Unterminated string constant");
|
||||
else if (ch == quote) break;
|
||||
ret += ch;
|
||||
}
|
||||
@@ -752,9 +755,9 @@ function parse($TEXT, options) {
|
||||
);
|
||||
};
|
||||
|
||||
function semicolon() {
|
||||
function semicolon(optional) {
|
||||
if (is("punc", ";")) next();
|
||||
else if (!can_insert_semicolon()) unexpected();
|
||||
else if (!optional && !can_insert_semicolon()) unexpected();
|
||||
};
|
||||
|
||||
function parenthesised() {
|
||||
@@ -843,7 +846,7 @@ function parse($TEXT, options) {
|
||||
case "do":
|
||||
return new AST_Do({
|
||||
body : in_loop(statement),
|
||||
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(), tmp)
|
||||
condition : (expect_token("keyword", "while"), tmp = parenthesised(), semicolon(true), tmp)
|
||||
});
|
||||
|
||||
case "while":
|
||||
@@ -1323,7 +1326,7 @@ function parse($TEXT, options) {
|
||||
ret = _make_symbol(AST_SymbolRef);
|
||||
break;
|
||||
case "num":
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value, literal: tok.literal });
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||
break;
|
||||
case "string":
|
||||
ret = new AST_String({
|
||||
|
||||
48
lib/scope.js
48
lib/scope.js
@@ -98,6 +98,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
// pass 1: setup scope chaining and handle definitions
|
||||
var self = this;
|
||||
var scope = self.parent_scope = null;
|
||||
var labels = new Dictionary();
|
||||
var defun = null;
|
||||
var nesting = 0;
|
||||
var in_destructuring = null;
|
||||
@@ -121,20 +122,24 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
node.init_scope_vars(nesting);
|
||||
var save_scope = node.parent_scope = scope;
|
||||
var save_defun = defun;
|
||||
var save_labels = labels;
|
||||
defun = scope = node;
|
||||
labels = new Dictionary();
|
||||
++nesting; descend(); --nesting;
|
||||
scope = save_scope;
|
||||
defun = save_defun;
|
||||
labels = save_labels;
|
||||
return true; // don't descend again in TreeWalker
|
||||
}
|
||||
if (node instanceof AST_Directive) {
|
||||
node.scope = scope;
|
||||
push_uniq(scope.directives, node.value);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Number) {
|
||||
node.scope = scope;
|
||||
return true;
|
||||
if (node instanceof AST_LabeledStatement) {
|
||||
var l = node.label;
|
||||
if (labels.has(l.name)) {
|
||||
throw new Error(string_template("Label {name} defined twice", l));
|
||||
}
|
||||
labels.set(l.name, l);
|
||||
descend();
|
||||
labels.del(l.name);
|
||||
return true; // no descend again
|
||||
}
|
||||
if (node instanceof AST_With) {
|
||||
for (var s = scope; s; s = s.parent_scope)
|
||||
@@ -148,6 +153,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
node.object_destructuring_arg = !!in_destructuring;
|
||||
defun.def_variable(node);
|
||||
}
|
||||
if (node instanceof AST_Label) {
|
||||
node.thedef = node;
|
||||
node.references = [];
|
||||
}
|
||||
if (node instanceof AST_SymbolLambda) {
|
||||
defun.def_function(node);
|
||||
}
|
||||
@@ -178,6 +187,15 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
(options.screw_ie8 ? scope : defun)
|
||||
.def_variable(node);
|
||||
}
|
||||
else if (node instanceof AST_LabelRef) {
|
||||
var sym = labels.get(node.name);
|
||||
if (!sym) throw new Error(string_template("Undefined label {name} [{line},{col}]", {
|
||||
name: node.name,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
}));
|
||||
node.thedef = sym;
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
|
||||
@@ -200,6 +218,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
cls = prev_cls;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_LoopControl && node.label) {
|
||||
node.label.thedef.references.push(node);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var name = node.name;
|
||||
var sym = node.scope.find_variable(name);
|
||||
@@ -236,7 +258,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||
this.directives = []; // contains the directives defined in this scope, i.e. "use strict"
|
||||
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||
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
|
||||
@@ -247,10 +268,6 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
|
||||
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("strict", function(){
|
||||
return this.has_directive("use strict");
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("init_scope_vars", function(){
|
||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
||||
this.uses_arguments = false;
|
||||
@@ -274,11 +291,6 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||
|| (this.parent_scope && this.parent_scope.find_variable(name));
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("has_directive", function(value){
|
||||
return this.parent_scope && this.parent_scope.has_directive(value)
|
||||
|| (this.directives.indexOf(value) >= 0 ? this : null);
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("def_function", function(symbol){
|
||||
this.functions.set(symbol.name, this.def_variable(symbol));
|
||||
});
|
||||
|
||||
@@ -70,7 +70,7 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
if (y !== undefined) x = y;
|
||||
}
|
||||
}
|
||||
tw.pop();
|
||||
tw.pop(this);
|
||||
return x;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "2.5.0",
|
||||
"version": "2.6.1",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -332,53 +332,247 @@ cond_7_1: {
|
||||
cond_8: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true
|
||||
evaluate : true,
|
||||
booleans : false
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
a = condition ? true : false;
|
||||
|
||||
a = !condition ? true : false;
|
||||
|
||||
a = condition() ? true : false;
|
||||
|
||||
a = condition ? !0 : !1;
|
||||
a = !condition ? !null : !2;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
if (condition) {
|
||||
a = true;
|
||||
} else {
|
||||
a = false;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !0;
|
||||
} else {
|
||||
a = !1;
|
||||
}
|
||||
|
||||
a = condition ? false : true;
|
||||
|
||||
a = !condition ? false : true;
|
||||
|
||||
a = condition() ? false : true;
|
||||
|
||||
a = condition ? !3 : !0;
|
||||
a = !condition ? !2 : !0;
|
||||
a = condition() ? !1 : !0;
|
||||
|
||||
if (condition) {
|
||||
a = false;
|
||||
} else {
|
||||
a = true;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !1;
|
||||
} else {
|
||||
a = !0;
|
||||
}
|
||||
|
||||
// don't compress these
|
||||
a = condition ? 1 : false;
|
||||
|
||||
a = !condition ? true : 0;
|
||||
|
||||
a = condition ? 1 : 0;
|
||||
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !!condition;
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !condition;
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = condition ? 0 : true;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
cond_8b: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : true,
|
||||
booleans : true
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
a = condition ? true : false;
|
||||
a = !condition ? true : false;
|
||||
a = condition() ? true : false;
|
||||
|
||||
a = condition ? !0 : !1;
|
||||
a = !condition ? !null : !2;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
if (condition) {
|
||||
a = true;
|
||||
} else {
|
||||
a = false;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !0;
|
||||
} else {
|
||||
a = !1;
|
||||
}
|
||||
|
||||
a = condition ? false : true;
|
||||
a = !condition ? false : true;
|
||||
a = condition() ? false : true;
|
||||
|
||||
a = condition ? !3 : !0;
|
||||
a = !condition ? !2 : !0;
|
||||
a = condition() ? !1 : !0;
|
||||
|
||||
if (condition) {
|
||||
a = false;
|
||||
} else {
|
||||
a = true;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !1;
|
||||
} else {
|
||||
a = !0;
|
||||
}
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = !condition ? true : 0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !!condition;
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !condition;
|
||||
|
||||
a = condition ? 1 : !1;
|
||||
a = condition ? 0 : !0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
cond_8c: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate : false,
|
||||
booleans : false
|
||||
};
|
||||
input: {
|
||||
var a;
|
||||
// compress these
|
||||
a = condition ? true : false;
|
||||
a = !condition ? true : false;
|
||||
a = condition() ? true : false;
|
||||
|
||||
a = condition ? !0 : !1;
|
||||
a = !condition ? !null : !2;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
if (condition) {
|
||||
a = true;
|
||||
} else {
|
||||
a = false;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !0;
|
||||
} else {
|
||||
a = !1;
|
||||
}
|
||||
|
||||
a = condition ? false : true;
|
||||
a = !condition ? false : true;
|
||||
a = condition() ? false : true;
|
||||
|
||||
a = condition ? !3 : !0;
|
||||
a = !condition ? !2 : !0;
|
||||
a = condition() ? !1 : !0;
|
||||
|
||||
if (condition) {
|
||||
a = false;
|
||||
} else {
|
||||
a = true;
|
||||
}
|
||||
|
||||
if (condition) {
|
||||
a = !1;
|
||||
} else {
|
||||
a = !0;
|
||||
}
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = !condition ? true : 0;
|
||||
a = condition ? 1 : 0;
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = !!condition();
|
||||
|
||||
a = !!condition;
|
||||
a = !condition;
|
||||
a = condition() ? !0 : !-3.5;
|
||||
|
||||
a = !!condition;
|
||||
a = !!condition;
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !!condition;
|
||||
a = !condition();
|
||||
|
||||
a = !condition;
|
||||
a = !condition;
|
||||
|
||||
a = condition ? 1 : false;
|
||||
a = condition ? 0 : true;
|
||||
a = condition ? 1 : 0;
|
||||
|
||||
@@ -121,3 +121,27 @@ drop_if_else_break_4: {
|
||||
for (; bar() && (x(), y(), foo());) baz(), z(), k();
|
||||
}
|
||||
}
|
||||
|
||||
parse_do_while_with_semicolon: {
|
||||
options = { loops: false };
|
||||
input: {
|
||||
do {
|
||||
x();
|
||||
} while (false);y()
|
||||
}
|
||||
expect: {
|
||||
do x(); while (false);y();
|
||||
}
|
||||
}
|
||||
|
||||
parse_do_while_without_semicolon: {
|
||||
options = { loops: false };
|
||||
input: {
|
||||
do {
|
||||
x();
|
||||
} while (false)y()
|
||||
}
|
||||
expect: {
|
||||
do x(); while (false);y();
|
||||
}
|
||||
}
|
||||
124
test/compress/return_undefined.js
Normal file
124
test/compress/return_undefined.js
Normal file
@@ -0,0 +1,124 @@
|
||||
return_undefined: {
|
||||
options = {
|
||||
sequences : false,
|
||||
if_return : true,
|
||||
evaluate : true,
|
||||
dead_code : true,
|
||||
conditionals : true,
|
||||
comparisons : true,
|
||||
booleans : true,
|
||||
unused : true,
|
||||
side_effects : true,
|
||||
properties : true,
|
||||
drop_debugger : true,
|
||||
loops : true,
|
||||
hoist_funs : true,
|
||||
keep_fargs : true,
|
||||
keep_fnames : false,
|
||||
hoist_vars : true,
|
||||
join_vars : true,
|
||||
cascade : true,
|
||||
negate_iife : true
|
||||
};
|
||||
input: {
|
||||
function f0() {
|
||||
}
|
||||
function f1() {
|
||||
return undefined;
|
||||
}
|
||||
function f2() {
|
||||
return void 0;
|
||||
}
|
||||
function f3() {
|
||||
return void 123;
|
||||
}
|
||||
function f4() {
|
||||
return;
|
||||
}
|
||||
function f5(a, b) {
|
||||
console.log(a, b);
|
||||
baz(a);
|
||||
return;
|
||||
}
|
||||
function f6(a, b) {
|
||||
console.log(a, b);
|
||||
if (a) {
|
||||
foo(b);
|
||||
baz(a);
|
||||
return a + b;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function f7(a, b) {
|
||||
console.log(a, b);
|
||||
if (a) {
|
||||
foo(b);
|
||||
baz(a);
|
||||
return void 0;
|
||||
}
|
||||
return a + b;
|
||||
}
|
||||
function f8(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
return void 0;
|
||||
}
|
||||
function f9(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
return undefined;
|
||||
}
|
||||
function f10() {
|
||||
return false;
|
||||
}
|
||||
function f11() {
|
||||
return null;
|
||||
}
|
||||
function f12() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f0() {}
|
||||
function f1() {}
|
||||
function f2() {}
|
||||
function f3() {}
|
||||
function f4() {}
|
||||
function f5(a, b) {
|
||||
console.log(a, b);
|
||||
baz(a);
|
||||
}
|
||||
function f6(a, b) {
|
||||
console.log(a, b);
|
||||
if (a) {
|
||||
foo(b);
|
||||
baz(a);
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
function f7(a, b) {
|
||||
console.log(a, b);
|
||||
if (!a)
|
||||
return a + b;
|
||||
foo(b);
|
||||
baz(a);
|
||||
}
|
||||
function f8(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
}
|
||||
function f9(a, b) {
|
||||
foo(a);
|
||||
bar(b);
|
||||
}
|
||||
function f10() {
|
||||
return !1;
|
||||
}
|
||||
function f11() {
|
||||
return null;
|
||||
}
|
||||
function f12() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
test/compress/screw-ie8.js
Normal file
18
test/compress/screw-ie8.js
Normal file
@@ -0,0 +1,18 @@
|
||||
do_screw: {
|
||||
options = { screw_ie8: true };
|
||||
beautify = {
|
||||
screw_ie8: true,
|
||||
ascii_only: true
|
||||
};
|
||||
|
||||
input: f("\v");
|
||||
expect_exact: 'f("\\v");';
|
||||
}
|
||||
|
||||
dont_screw: {
|
||||
options = { screw_ie8: false };
|
||||
beautify = { screw_ie8: false, ascii_only: true };
|
||||
|
||||
input: f("\v");
|
||||
expect_exact: 'f("\\x0B");';
|
||||
}
|
||||
@@ -4,7 +4,6 @@ var U = require("../tools/node");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var assert = require("assert");
|
||||
var sys = require("util");
|
||||
|
||||
var tests_dir = path.dirname(module.filename);
|
||||
var failures = 0;
|
||||
@@ -12,8 +11,8 @@ var failed_files = {};
|
||||
|
||||
run_compress_tests();
|
||||
if (failures) {
|
||||
sys.error("\n!!! Failed " + failures + " test cases.");
|
||||
sys.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
console.error("\n!!! Failed " + failures + " test cases.");
|
||||
console.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -91,14 +90,15 @@ function run_compress_tests() {
|
||||
warnings: false
|
||||
});
|
||||
var cmp = new U.Compressor(options, true);
|
||||
var output_options = test.beautify || {};
|
||||
var expect;
|
||||
if (test.expect) {
|
||||
expect = make_code(as_toplevel(test.expect), false);
|
||||
expect = make_code(as_toplevel(test.expect), output_options);
|
||||
} else {
|
||||
expect = test.expect_exact;
|
||||
}
|
||||
var input = as_toplevel(test.input);
|
||||
var input_code = make_code(test.input);
|
||||
var input_code = make_code(test.input, { beautify: true });
|
||||
if (test.mangle_props) {
|
||||
input = U.mangle_properties(input, test.mangle_props);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ function run_compress_tests() {
|
||||
if (test.mangle) {
|
||||
output.mangle_names(test.mangle);
|
||||
}
|
||||
output = make_code(output, false);
|
||||
output = make_code(output, output_options);
|
||||
if (expect != output) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||
input: input_code,
|
||||
@@ -151,7 +151,7 @@ function parse_test(file) {
|
||||
file: file,
|
||||
line: node.start.line,
|
||||
col: node.start.col,
|
||||
code: make_code(node, false)
|
||||
code: make_code(node, { beautify: false })
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -198,15 +198,15 @@ function parse_test(file) {
|
||||
};
|
||||
}
|
||||
|
||||
function make_code(ast, beautify) {
|
||||
if (arguments.length == 1) beautify = true;
|
||||
var stream = U.OutputStream({ beautify: beautify, inline_script: true });
|
||||
function make_code(ast, options) {
|
||||
options.inline_script = true;
|
||||
var stream = U.OutputStream(options);
|
||||
ast.print(stream);
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
function evaluate(code) {
|
||||
if (code instanceof U.AST_Node)
|
||||
code = make_code(code);
|
||||
code = make_code(code, { beautify: true });
|
||||
return new Function("return(" + code + ")")();
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ exports.minify = function(files, options) {
|
||||
UglifyJS.base54.reset();
|
||||
|
||||
// 1. parse
|
||||
var haveScope = false;
|
||||
var toplevel = null,
|
||||
sourcesContent = {};
|
||||
|
||||
@@ -74,7 +73,6 @@ exports.minify = function(files, options) {
|
||||
var compress = { warnings: options.warnings };
|
||||
UglifyJS.merge(compress, options.compress);
|
||||
toplevel.figure_out_scope();
|
||||
haveScope = true;
|
||||
var sq = UglifyJS.Compressor(compress);
|
||||
toplevel = toplevel.transform(sq);
|
||||
}
|
||||
@@ -82,17 +80,11 @@ exports.minify = function(files, options) {
|
||||
// 3. mangle
|
||||
if (options.mangle) {
|
||||
toplevel.figure_out_scope(options.mangle);
|
||||
haveScope = true;
|
||||
toplevel.compute_char_frequency(options.mangle);
|
||||
toplevel.mangle_names(options.mangle);
|
||||
}
|
||||
|
||||
// 4. scope (if needed)
|
||||
if (!haveScope) {
|
||||
toplevel.figure_out_scope();
|
||||
}
|
||||
|
||||
// 5. output
|
||||
// 4. output
|
||||
var inMap = options.inSourceMap;
|
||||
var output = {};
|
||||
if (typeof options.inSourceMap == "string") {
|
||||
|
||||
Reference in New Issue
Block a user