uglify-js
Advanced tools
Comparing version
@@ -200,2 +200,6 @@ /*********************************************************************** | ||
var AST_IterationStatement = DEFNODE("IterationStatement", null, { | ||
$documentation: "Internal class. All loops inherit from it." | ||
}, AST_StatementWithBody); | ||
var AST_DWLoop = DEFNODE("DWLoop", "condition", { | ||
@@ -212,3 +216,3 @@ $documentation: "Base class for do/while statements", | ||
} | ||
}, AST_StatementWithBody); | ||
}, AST_IterationStatement); | ||
@@ -238,3 +242,3 @@ var AST_Do = DEFNODE("Do", null, { | ||
} | ||
}, AST_StatementWithBody); | ||
}, AST_IterationStatement); | ||
@@ -255,3 +259,3 @@ var AST_ForIn = DEFNODE("ForIn", "init name object", { | ||
} | ||
}, AST_StatementWithBody); | ||
}, AST_IterationStatement); | ||
@@ -828,3 +832,7 @@ var AST_With = DEFNODE("With", "expression", { | ||
$propdoc: { | ||
references: "[AST_LabelRef*] a list of nodes referring to this label" | ||
references: "[AST_LoopControl*] a list of nodes referring to this label" | ||
}, | ||
initialize: function() { | ||
this.references = []; | ||
this.thedef = this; | ||
} | ||
@@ -976,19 +984,13 @@ }, AST_Symbol); | ||
var stack = this.stack; | ||
if (label) { | ||
for (var i = stack.length; --i >= 0;) { | ||
var x = stack[i]; | ||
if (x instanceof AST_LabeledStatement && x.label.name == label.name) { | ||
return x.body; | ||
} | ||
if (label) for (var i = stack.length; --i >= 0;) { | ||
var x = stack[i]; | ||
if (x instanceof AST_LabeledStatement && x.label.name == label.name) { | ||
return x.body; | ||
} | ||
} else { | ||
for (var i = stack.length; --i >= 0;) { | ||
var x = stack[i]; | ||
if (x instanceof AST_Switch | ||
|| x instanceof AST_For | ||
|| x instanceof AST_ForIn | ||
|| x instanceof AST_DWLoop) return x; | ||
} | ||
} else for (var i = stack.length; --i >= 0;) { | ||
var x = stack[i]; | ||
if (x instanceof AST_Switch || x instanceof AST_IterationStatement) | ||
return x; | ||
} | ||
} | ||
}; |
@@ -402,3 +402,3 @@ /*********************************************************************** | ||
comments.forEach(function(c){ | ||
if (c.type == "comment1") { | ||
if (/comment[134]/.test(c.type)) { | ||
output.print("//" + c.value + "\n"); | ||
@@ -993,3 +993,14 @@ output.indent(); | ||
output.print(self.operator); | ||
output.space(); | ||
if (self.operator == "<" | ||
&& self.right instanceof AST_UnaryPrefix | ||
&& self.right.operator == "!" | ||
&& self.right.expression instanceof AST_UnaryPrefix | ||
&& self.right.expression.operator == "--") { | ||
// space is mandatory to avoid outputting <!-- | ||
// http://javascript.spec.whatwg.org/#comment-syntax | ||
output.print(" "); | ||
} else { | ||
// the space is optional depending on "beautify" | ||
output.space(); | ||
} | ||
self.right.print(output); | ||
@@ -996,0 +1007,0 @@ }); |
100
lib/parse.js
@@ -173,3 +173,3 @@ /*********************************************************************** | ||
if (i == 0) return false; | ||
if (is_digit(str.charCodeAt(0))) return false; | ||
if (!is_identifier_start(str.charCodeAt(0))) return false; | ||
while (--i >= 0) { | ||
@@ -214,3 +214,3 @@ if (!is_identifier_char(str.charAt(i))) | ||
function tokenizer($TEXT, filename) { | ||
function tokenizer($TEXT, filename, html5_comments) { | ||
@@ -247,2 +247,10 @@ var S = { | ||
function forward(i) { | ||
while (i-- > 0) next(); | ||
}; | ||
function looking_at(str) { | ||
return S.text.substr(S.pos, str.length) == str; | ||
}; | ||
function find(what, signal_eof) { | ||
@@ -260,2 +268,3 @@ var pos = S.text.indexOf(what, S.pos); | ||
var prev_was_dot = false; | ||
function token(type, value, is_comment) { | ||
@@ -265,2 +274,3 @@ S.regex_allowed = ((type == "operator" && !UNARY_POSTFIX(value)) || | ||
(type == "punc" && PUNC_BEFORE_EXPRESSION(value))); | ||
prev_was_dot = (type == "punc" && value == "."); | ||
var ret = { | ||
@@ -387,4 +397,4 @@ type : type, | ||
function read_line_comment() { | ||
next(); | ||
function skip_line_comment(type) { | ||
var regex_allowed = S.regex_allowed; | ||
var i = find("\n"), ret; | ||
@@ -398,7 +408,9 @@ if (i == -1) { | ||
} | ||
return token("comment1", ret, true); | ||
S.comments_before.push(token(type, ret, true)); | ||
S.regex_allowed = regex_allowed; | ||
return next_token(); | ||
}; | ||
var read_multiline_comment = with_eof_error("Unterminated multiline comment", function(){ | ||
next(); | ||
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){ | ||
var regex_allowed = S.regex_allowed; | ||
var i = find("*/", true); | ||
@@ -413,4 +425,7 @@ var text = S.text.substring(S.pos, i); | ||
S.col += 2; | ||
S.newline_before = S.newline_before || text.indexOf("\n") >= 0; | ||
return token("comment2", text, true); | ||
var nlb = S.newline_before = S.newline_before || text.indexOf("\n") >= 0; | ||
S.comments_before.push(token("comment2", text, true)); | ||
S.regex_allowed = regex_allowed; | ||
S.newline_before = nlb; | ||
return next_token(); | ||
}); | ||
@@ -479,12 +494,9 @@ | ||
next(); | ||
var regex_allowed = S.regex_allowed; | ||
switch (peek()) { | ||
case "/": | ||
S.comments_before.push(read_line_comment()); | ||
S.regex_allowed = regex_allowed; | ||
return next_token(); | ||
next(); | ||
return skip_line_comment("comment1"); | ||
case "*": | ||
S.comments_before.push(read_multiline_comment()); | ||
S.regex_allowed = regex_allowed; | ||
return next_token(); | ||
next(); | ||
return skip_multiline_comment(); | ||
} | ||
@@ -503,2 +515,3 @@ return S.regex_allowed ? read_regexp("") : read_operator("/"); | ||
var word = read_name(); | ||
if (prev_was_dot) return token("name", word); | ||
return KEYWORDS_ATOM(word) ? token("atom", word) | ||
@@ -526,2 +539,12 @@ : !KEYWORDS(word) ? token("name", word) | ||
start_token(); | ||
if (html5_comments) { | ||
if (looking_at("<!--")) { | ||
forward(4); | ||
return skip_line_comment("comment3"); | ||
} | ||
if (looking_at("-->") && S.newline_before) { | ||
forward(3); | ||
return skip_line_comment("comment4"); | ||
} | ||
} | ||
var ch = peek(); | ||
@@ -602,10 +625,14 @@ if (!ch) return token("eof"); | ||
options = defaults(options, { | ||
strict : false, | ||
filename : null, | ||
toplevel : null, | ||
expression : false | ||
strict : false, | ||
filename : null, | ||
toplevel : null, | ||
expression : false, | ||
html5_comments : true, | ||
}); | ||
var S = { | ||
input : typeof $TEXT == "string" ? tokenizer($TEXT, options.filename) : $TEXT, | ||
input : (typeof $TEXT == "string" | ||
? tokenizer($TEXT, options.filename, | ||
options.html5_comments) | ||
: $TEXT), | ||
token : null, | ||
@@ -703,4 +730,3 @@ prev : null, | ||
var statement = embed_tokens(function() { | ||
var tmp; | ||
function handle_regexp() { | ||
if (is("operator", "/") || is("operator", "/=")) { | ||
@@ -710,2 +736,7 @@ S.peeked = null; | ||
} | ||
}; | ||
var statement = embed_tokens(function() { | ||
var tmp; | ||
handle_regexp(); | ||
switch (S.token.type) { | ||
@@ -838,2 +869,14 @@ case "string": | ||
S.labels.pop(); | ||
if (!(stat instanceof AST_IterationStatement)) { | ||
// check for `continue` that refers to this label. | ||
// those should be reported as syntax errors. | ||
// https://github.com/mishoo/UglifyJS2/issues/287 | ||
label.references.forEach(function(ref){ | ||
if (ref instanceof AST_Continue) { | ||
ref = ref.label.start; | ||
croak("Continue label `" + label.name + "` refers to non-IterationStatement.", | ||
ref.line, ref.col, ref.pos); | ||
} | ||
}); | ||
} | ||
return new AST_LabeledStatement({ body: stat, label: label }); | ||
@@ -847,3 +890,3 @@ }; | ||
function break_cont(type) { | ||
var label = null; | ||
var label = null, ldef; | ||
if (!can_insert_semicolon()) { | ||
@@ -853,4 +896,6 @@ label = as_symbol(AST_LabelRef, true); | ||
if (label != null) { | ||
if (!find_if(function(l){ return l.name == label.name }, S.labels)) | ||
ldef = find_if(function(l){ return l.name == label.name }, S.labels); | ||
if (!ldef) | ||
croak("Undefined label " + label.name); | ||
label.thedef = ldef; | ||
} | ||
@@ -860,3 +905,5 @@ else if (S.in_loop == 0) | ||
semicolon(); | ||
return new type({ label: label }); | ||
var stat = new type({ label: label }); | ||
if (ldef) ldef.references.push(stat); | ||
return stat; | ||
}; | ||
@@ -1300,2 +1347,3 @@ | ||
next(); | ||
handle_regexp(); | ||
var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls)); | ||
@@ -1302,0 +1350,0 @@ ex.start = start; |
@@ -85,3 +85,2 @@ /*********************************************************************** | ||
var scope = self.parent_scope = null; | ||
var labels = new Dictionary(); | ||
var nesting = 0; | ||
@@ -92,8 +91,5 @@ var tw = new TreeWalker(function(node, descend){ | ||
var save_scope = node.parent_scope = scope; | ||
var save_labels = labels; | ||
++nesting; | ||
scope = node; | ||
labels = new Dictionary(); | ||
descend(); | ||
labels = save_labels; | ||
scope = save_scope; | ||
@@ -113,18 +109,5 @@ --nesting; | ||
} | ||
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_Symbol) { | ||
node.scope = scope; | ||
} | ||
if (node instanceof AST_Label) { | ||
node.thedef = node; | ||
node.init_scope_vars(); | ||
} | ||
if (node instanceof AST_SymbolLambda) { | ||
@@ -156,11 +139,2 @@ scope.def_function(node); | ||
} | ||
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; | ||
} | ||
}); | ||
@@ -180,6 +154,2 @@ self.walk(tw); | ||
} | ||
if (node instanceof AST_LabelRef) { | ||
node.reference(); | ||
return true; | ||
} | ||
if (node instanceof AST_SymbolRef) { | ||
@@ -203,3 +173,3 @@ var name = node.name; | ||
} | ||
if (name == "arguments") { | ||
if (func && name == "arguments") { | ||
func.uses_arguments = true; | ||
@@ -250,10 +220,2 @@ } | ||
AST_Label.DEFMETHOD("init_scope_vars", function(){ | ||
this.references = []; | ||
}); | ||
AST_LabelRef.DEFMETHOD("reference", function(){ | ||
this.thedef.references.push(this); | ||
}); | ||
AST_Scope.DEFMETHOD("find_variable", function(name){ | ||
@@ -260,0 +222,0 @@ if (name instanceof AST_Symbol) name = name.name; |
@@ -6,3 +6,3 @@ { | ||
"main": "tools/node.js", | ||
"version": "2.4.0", | ||
"version": "2.4.1", | ||
"engines": { "node" : ">=0.4.0" }, | ||
@@ -9,0 +9,0 @@ "maintainers": [{ |
@@ -187,27 +187,44 @@ UglifyJS 2 | ||
- `sequences` -- join consecutive simple statements using the comma operator | ||
- `properties` -- rewrite property access using the dot notation, for | ||
example `foo["bar"] → foo.bar` | ||
- `dead_code` -- remove unreachable code | ||
- `drop_debugger` -- remove `debugger;` statements | ||
- `unsafe` (default: false) -- apply "unsafe" transformations (discussion below) | ||
- `conditionals` -- apply optimizations for `if`-s and conditional | ||
expressions | ||
- `comparisons` -- apply certain optimizations to binary nodes, for example: | ||
`!(a <= b) → a > b` (only when `unsafe`), attempts to negate binary nodes, | ||
e.g. `a = !b && !c && !d && !e → a=!(b||c||d||e)` etc. | ||
- `evaluate` -- attempt to evaluate constant expressions | ||
- `booleans` -- various optimizations for boolean context, for example `!!a | ||
? b : c → a ? b : c` | ||
- `loops` -- optimizations for `do`, `while` and `for` loops when we can | ||
statically determine the condition | ||
- `unused` -- drop unreferenced functions and variables | ||
- `hoist_funs` -- hoist function declarations | ||
- `hoist_vars` (default: false) -- hoist `var` declarations (this is `false` | ||
by default because it seems to increase the size of the output in general) | ||
- `if_return` -- optimizations for if/return and if/continue | ||
- `join_vars` -- join consecutive `var` statements | ||
- `cascade` -- small optimization for sequences, transform `x, x` into `x` | ||
and `x = something(), x` into `x = something()` | ||
- `warnings` -- display warnings when dropping unreachable code or unused | ||
declarations etc. | ||
- `negate_iife` -- negate "Immediately-Called Function Expressions" | ||
@@ -217,2 +234,17 @@ where the return value is discarded, to avoid the parens that the | ||
- `pure_getters` -- the default is `false`. If you pass `true` for | ||
this, UglifyJS will assume that object property access | ||
(e.g. `foo.bar` or `foo["bar"]`) doesn't have any side effects. | ||
- `pure_funcs` -- default `null`. You can pass an array of names and | ||
UglifyJS will assume that those functions do not produce side | ||
effects. DANGER: will not check if the name is redefined in scope. | ||
An example case here, for instance `var q = Math.floor(a/b)`. If | ||
variable `q` is not used elsewhere, UglifyJS will drop it, but will | ||
still keep the `Math.floor(a/b)`, not knowing what it does. You can | ||
pass `pure_funcs: [ 'Math.floor' ]` to let it know that this | ||
function won't produce any side effect, in which case the whole | ||
statement would get discarded. The current implementation adds some | ||
overhead (compression will be slower). | ||
### The `unsafe` option | ||
@@ -219,0 +251,0 @@ |
@@ -15,1 +15,61 @@ holes_and_undefined: { | ||
} | ||
constant_join: { | ||
options = { | ||
unsafe : true, | ||
evaluate : true | ||
}; | ||
input: { | ||
var a = [ "foo", "bar", "baz" ].join(""); | ||
var a1 = [ "foo", "bar", "baz" ].join(); | ||
var b = [ "foo", 1, 2, 3, "bar" ].join(""); | ||
var c = [ boo(), "foo", 1, 2, 3, "bar", bar() ].join(""); | ||
var c1 = [ boo(), bar(), "foo", 1, 2, 3, "bar", bar() ].join(""); | ||
var c2 = [ 1, 2, "foo", "bar", baz() ].join(""); | ||
var d = [ "foo", 1 + 2 + "bar", "baz" ].join("-"); | ||
var e = [].join(foo + bar); | ||
var f = [].join(""); | ||
var g = [].join("foo"); | ||
} | ||
expect: { | ||
var a = "foobarbaz"; | ||
var a1 = "foo,bar,baz"; | ||
var b = "foo123bar"; | ||
var c = boo() + "foo123bar" + bar(); | ||
var c1 = "" + boo() + bar() + "foo123bar" + bar(); | ||
var c2 = "12foobar" + baz(); | ||
var d = "foo-3bar-baz"; | ||
var e = [].join(foo + bar); | ||
var f = ""; | ||
var g = ""; | ||
} | ||
} | ||
constant_join_2: { | ||
options = { | ||
unsafe : true, | ||
evaluate : true | ||
}; | ||
input: { | ||
var a = [ "foo", "bar", boo(), "baz", "x", "y" ].join(""); | ||
var b = [ "foo", "bar", boo(), "baz", "x", "y" ].join("-"); | ||
var c = [ "foo", "bar", boo(), "baz", "x", "y" ].join("really-long-separator"); | ||
var d = [ "foo", "bar", boo(), | ||
[ "foo", 1, 2, 3, "bar" ].join("+"), | ||
"baz", "x", "y" ].join("-"); | ||
var e = [ "foo", "bar", boo(), | ||
[ "foo", 1, 2, 3, "bar" ].join("+"), | ||
"baz", "x", "y" ].join("really-long-separator"); | ||
var f = [ "str", "str" + variable, "foo", "bar", "moo" + foo ].join(""); | ||
} | ||
expect: { | ||
var a = "foobar" + boo() + "bazxy"; | ||
var b = [ "foo-bar", boo(), "baz-x-y" ].join("-"); | ||
var c = [ "foo", "bar", boo(), "baz", "x", "y" ].join("really-long-separator"); | ||
var d = [ "foo-bar", boo(), "foo+1+2+3+bar-baz-x-y" ].join("-"); | ||
var e = [ "foo", "bar", boo(), | ||
"foo+1+2+3+bar", | ||
"baz", "x", "y" ].join("really-long-separator"); | ||
var f = "strstr" + variable + "foobarmoo" + foo; | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
355231
4.13%38
5.56%8509
3.13%626
5.39%