Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4eb4cb656c | ||
|
|
193612ac67 | ||
|
|
95cfce68ea | ||
|
|
ec4202590d | ||
|
|
5e2cd07d6f | ||
|
|
06166df999 | ||
|
|
e2dc9cf091 | ||
|
|
069df27bf1 | ||
|
|
3e7873217c | ||
|
|
e21bab7ce6 | ||
|
|
ac9a168fba | ||
|
|
81b64549ce | ||
|
|
082e004b87 | ||
|
|
983e69128b | ||
|
|
b335912e86 | ||
|
|
cc07f3b806 | ||
|
|
07e4b64f3a | ||
|
|
d3ce2bc9e7 | ||
|
|
cff3bf4914 | ||
|
|
79cfac77bd | ||
|
|
224c14d49d | ||
|
|
7857354d85 | ||
|
|
b4aef753e7 | ||
|
|
424173d311 | ||
|
|
ec7cd1dcf7 | ||
|
|
7def684730 | ||
|
|
10f961c27b | ||
|
|
b483678ca7 | ||
|
|
cbbe6fad60 | ||
|
|
f96929c031 | ||
|
|
2b6657e967 | ||
|
|
7c0c92943f | ||
|
|
62a66dfff4 | ||
|
|
2cab348341 |
11
README.md
11
README.md
@@ -70,7 +70,7 @@ a double dash to prevent input files being used as option arguments:
|
||||
`debug` Add debug prefix and suffix.
|
||||
`domprops` Mangle property names that overlaps
|
||||
with DOM properties.
|
||||
`keep_quoted` Only mangle unquoted properies.
|
||||
`keep_quoted` Only mangle unquoted properties.
|
||||
`regex` Only mangle matched property names.
|
||||
`reserved` List of names that should not be mangled.
|
||||
-b, --beautify [options] Beautify output/specify output options:
|
||||
@@ -737,7 +737,7 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
comparison are switching. Compression only works if both `comparisons` and
|
||||
`unsafe_comps` are both set to true.
|
||||
|
||||
- `unsafe_Func` (default: `false`) -- compress and mangle `Function(args, code)`
|
||||
- `unsafe_Function` (default: `false`) -- compress and mangle `Function(args, code)`
|
||||
when both `args` and `code` are string literals.
|
||||
|
||||
- `unsafe_math` (default: `false`) -- optimize numerical expressions like
|
||||
@@ -749,6 +749,10 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
- `unsafe_regexp` (default: `false`) -- enable substitutions of variables with
|
||||
`RegExp` values the same way as if they are constants.
|
||||
|
||||
- `unsafe_undefined` (default: `false`) -- substitute `void 0` if there is a
|
||||
variable named `undefined` in scope (variable name will be mangled, typically
|
||||
reduced to a single character)
|
||||
|
||||
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
|
||||
direct variable assignments do not count as references unless set to `"keep_assign"`)
|
||||
|
||||
@@ -922,9 +926,6 @@ when this flag is on:
|
||||
- `new Object()` → `{}`
|
||||
- `String(exp)` or `exp.toString()` → `"" + exp`
|
||||
- `new Object/RegExp/Function/Error/Array (...)` → we discard the `new`
|
||||
- `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)
|
||||
|
||||
### Conditional compilation
|
||||
|
||||
|
||||
35
bin/uglifyjs
35
bin/uglifyjs
@@ -43,6 +43,7 @@ program.option("-d, --define <expr>[=value]", "Global definitions.", parse_js("d
|
||||
program.option("--ie8", "Support non-standard Internet Explorer 8.");
|
||||
program.option("--keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.");
|
||||
program.option("--name-cache <file>", "File to hold mangled name mappings.");
|
||||
program.option("--rename", "Force symbol expansion.");
|
||||
program.option("--no-rename", "Disable symbol expansion.");
|
||||
program.option("--self", "Build UglifyJS as a library (implies --wrap UglifyJS)");
|
||||
program.option("--source-map [options]", "Enable source map/specify source map options.", parse_source_map());
|
||||
@@ -62,13 +63,11 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
|
||||
"compress",
|
||||
"ie8",
|
||||
"mangle",
|
||||
"rename",
|
||||
"sourceMap",
|
||||
"toplevel",
|
||||
"wrap"
|
||||
].forEach(function(name) {
|
||||
if (name in program) {
|
||||
if (name == "rename" && program[name]) return;
|
||||
options[name] = program[name];
|
||||
}
|
||||
});
|
||||
@@ -121,6 +120,11 @@ if (program.parse) {
|
||||
fatal("ERROR: inline source map only works with built-in parser");
|
||||
}
|
||||
}
|
||||
if (~program.rawArgs.indexOf("--rename")) {
|
||||
options.rename = true;
|
||||
} else if (!program.rename) {
|
||||
options.rename = false;
|
||||
}
|
||||
var convert_path = function(name) {
|
||||
return name;
|
||||
};
|
||||
@@ -227,28 +231,15 @@ function run() {
|
||||
result.ast.figure_out_scope({});
|
||||
}
|
||||
print(JSON.stringify(result.ast, function(key, value) {
|
||||
switch (key) {
|
||||
if (value) switch (key) {
|
||||
case "thedef":
|
||||
if (typeof value == "object" && typeof value.id == "number") {
|
||||
return value.id;
|
||||
}
|
||||
return;
|
||||
return symdef(value);
|
||||
case "enclosed":
|
||||
return value.map(function(sym){
|
||||
return sym.id;
|
||||
});
|
||||
return value.length ? value.map(symdef) : undefined;
|
||||
case "variables":
|
||||
case "functions":
|
||||
case "globals":
|
||||
if (value && value.size()) {
|
||||
var ret = {};
|
||||
value.each(function(val, key) {
|
||||
// key/val inverted for readability.
|
||||
ret[val.id] = key;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
return;
|
||||
return value.size() ? value.map(symdef) : undefined;
|
||||
}
|
||||
if (skip_key(key)) return;
|
||||
if (value instanceof UglifyJS.AST_Token) return;
|
||||
@@ -403,6 +394,12 @@ function skip_key(key) {
|
||||
return skip_keys.indexOf(key) >= 0;
|
||||
}
|
||||
|
||||
function symdef(def) {
|
||||
var ret = (1e6 + def.id) + " " + def.name;
|
||||
if (def.mangled_name) ret += " " + def.mangled_name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
function format_object(obj) {
|
||||
var lines = [];
|
||||
var padding = "";
|
||||
|
||||
14
lib/ast.js
14
lib/ast.js
@@ -267,11 +267,10 @@ var AST_For = DEFNODE("For", "init condition step", {
|
||||
}
|
||||
}, AST_IterationStatement);
|
||||
|
||||
var AST_ForIn = DEFNODE("ForIn", "init name object", {
|
||||
var AST_ForIn = DEFNODE("ForIn", "init object", {
|
||||
$documentation: "A `for ... in` statement",
|
||||
$propdoc: {
|
||||
init: "[AST_Node] the `for/in` initialization code",
|
||||
name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
|
||||
object: "[AST_Node] the object that we're looping through"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
@@ -309,6 +308,13 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
|
||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||
},
|
||||
clone: function(deep) {
|
||||
var node = this._clone(deep);
|
||||
if (this.variables) node.variables = this.variables.clone();
|
||||
if (this.functions) node.functions = this.functions.clone();
|
||||
if (this.enclosed) node.enclosed = this.enclosed.slice();
|
||||
return node;
|
||||
}
|
||||
}, AST_Block);
|
||||
|
||||
var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
@@ -683,8 +689,8 @@ var AST_Object = DEFNODE("Object", "properties", {
|
||||
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||
$documentation: "Base class for literal object properties",
|
||||
$propdoc: {
|
||||
key: "[string] the property name converted to a string for ObjectKeyVal. For setters and getters this is an AST_SymbolAccessor.",
|
||||
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
|
||||
key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.",
|
||||
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
|
||||
410
lib/compress.js
410
lib/compress.js
@@ -84,10 +84,11 @@ function Compressor(options, false_by_default) {
|
||||
typeofs : !false_by_default,
|
||||
unsafe : false,
|
||||
unsafe_comps : false,
|
||||
unsafe_Func : false,
|
||||
unsafe_Function: false,
|
||||
unsafe_math : false,
|
||||
unsafe_proto : false,
|
||||
unsafe_regexp : false,
|
||||
unsafe_undefined: false,
|
||||
unused : !false_by_default,
|
||||
warnings : false,
|
||||
}, true);
|
||||
@@ -171,7 +172,8 @@ merge(Compressor.prototype, {
|
||||
node.process_expression(true);
|
||||
}
|
||||
var passes = +this.options.passes || 1;
|
||||
var last_count = 1 / 0;
|
||||
var min_count = 1 / 0;
|
||||
var stopping = false;
|
||||
var mangle = { ie8: this.option("ie8") };
|
||||
for (var pass = 0; pass < passes; pass++) {
|
||||
node.figure_out_scope(mangle);
|
||||
@@ -183,9 +185,15 @@ merge(Compressor.prototype, {
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
this.info("pass " + pass + ": last_count: " + last_count + ", count: " + count);
|
||||
if (count >= last_count) break;
|
||||
last_count = count;
|
||||
this.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
} else if (stopping) {
|
||||
break;
|
||||
} else {
|
||||
stopping = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.option("expression")) {
|
||||
@@ -397,14 +405,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function read_property(obj, key) {
|
||||
if (key instanceof AST_Constant) key = key.getValue();
|
||||
if (key instanceof AST_Node) return null;
|
||||
key = get_value(key);
|
||||
if (key instanceof AST_Node) return;
|
||||
var value;
|
||||
if (obj instanceof AST_Array) {
|
||||
var elements = obj.elements;
|
||||
if (key == "length") return make_node_from_constant(elements.length, obj);
|
||||
if (typeof key == "number" && key in elements) value = elements[key];
|
||||
} else if (obj instanceof AST_Object) {
|
||||
key = "" + key;
|
||||
var props = obj.properties;
|
||||
for (var i = props.length; --i >= 0;) {
|
||||
var prop = props[i];
|
||||
@@ -840,9 +849,8 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
|
||||
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);
|
||||
if (x instanceof AST_IterationStatement) {
|
||||
return x.body instanceof AST_BlockStatement ? x.body : x;
|
||||
}
|
||||
return x;
|
||||
};
|
||||
@@ -886,7 +894,7 @@ merge(Compressor.prototype, {
|
||||
sequencesize_2(statements, compressor);
|
||||
}
|
||||
if (compressor.option("join_vars")) {
|
||||
join_consecutive_vars(statements, compressor);
|
||||
join_consecutive_vars(statements);
|
||||
}
|
||||
if (compressor.option("collapse_vars")) {
|
||||
collapse(statements, compressor);
|
||||
@@ -1174,14 +1182,31 @@ merge(Compressor.prototype, {
|
||||
expr.definitions.forEach(extract_candidates);
|
||||
} else if (expr instanceof AST_DWLoop) {
|
||||
extract_candidates(expr.condition);
|
||||
if (!(expr.body instanceof AST_Block)) {
|
||||
extract_candidates(expr.body);
|
||||
}
|
||||
} else if (expr instanceof AST_Exit) {
|
||||
if (expr.value) extract_candidates(expr.value);
|
||||
} else if (expr instanceof AST_For) {
|
||||
if (expr.init) extract_candidates(expr.init);
|
||||
if (expr.condition) extract_candidates(expr.condition);
|
||||
if (expr.step) extract_candidates(expr.step);
|
||||
if (!(expr.body instanceof AST_Block)) {
|
||||
extract_candidates(expr.body);
|
||||
}
|
||||
} else if (expr instanceof AST_ForIn) {
|
||||
extract_candidates(expr.object);
|
||||
if (!(expr.body instanceof AST_Block)) {
|
||||
extract_candidates(expr.body);
|
||||
}
|
||||
} else if (expr instanceof AST_If) {
|
||||
extract_candidates(expr.condition);
|
||||
if (!(expr.body instanceof AST_Block)) {
|
||||
extract_candidates(expr.body);
|
||||
}
|
||||
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
|
||||
extract_candidates(expr.alternative);
|
||||
}
|
||||
} else if (expr instanceof AST_Sequence) {
|
||||
expr.expressions.forEach(extract_candidates);
|
||||
} else if (expr instanceof AST_SimpleStatement) {
|
||||
@@ -1208,10 +1233,12 @@ merge(Compressor.prototype, {
|
||||
if (parent instanceof AST_Call) return node;
|
||||
if (parent instanceof AST_Case) return node;
|
||||
if (parent instanceof AST_Conditional) return node;
|
||||
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
|
||||
if (parent instanceof AST_Exit) return node;
|
||||
if (parent instanceof AST_If) return node;
|
||||
if (parent instanceof AST_IterationStatement) return node;
|
||||
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
|
||||
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
|
||||
if (parent instanceof AST_Switch) return node;
|
||||
if (parent instanceof AST_VarDef) return node;
|
||||
return null;
|
||||
@@ -1615,7 +1642,7 @@ merge(Compressor.prototype, {
|
||||
var stat = null;
|
||||
for (var i = 0, len = block.body.length; i < len; i++) {
|
||||
var line = block.body[i];
|
||||
if (line instanceof AST_Definitions && declarations_only(line)) {
|
||||
if (line instanceof AST_Var && declarations_only(line)) {
|
||||
decls.push(line);
|
||||
} else if (stat) {
|
||||
return false;
|
||||
@@ -1637,37 +1664,34 @@ merge(Compressor.prototype, {
|
||||
for (var i = 0; i < statements.length; i++) {
|
||||
var stat = statements[i];
|
||||
if (prev) {
|
||||
if (stat instanceof AST_For && !(stat.init instanceof AST_Definitions)) {
|
||||
var abort = false;
|
||||
prev.body.walk(new TreeWalker(function(node) {
|
||||
if (abort || node instanceof AST_Scope) return true;
|
||||
if (node instanceof AST_Binary && node.operator == "in") {
|
||||
abort = true;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
if (!abort) {
|
||||
if (stat.init) stat.init = cons_seq(stat.init);
|
||||
else {
|
||||
stat.init = prev.body;
|
||||
n--;
|
||||
CHANGED = true;
|
||||
if (stat instanceof AST_Exit) {
|
||||
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
|
||||
} else if (stat instanceof AST_For) {
|
||||
if (!(stat.init instanceof AST_Definitions)) {
|
||||
var abort = false;
|
||||
prev.body.walk(new TreeWalker(function(node) {
|
||||
if (abort || node instanceof AST_Scope) return true;
|
||||
if (node instanceof AST_Binary && node.operator == "in") {
|
||||
abort = true;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
if (!abort) {
|
||||
if (stat.init) stat.init = cons_seq(stat.init);
|
||||
else {
|
||||
stat.init = prev.body;
|
||||
n--;
|
||||
CHANGED = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (stat instanceof AST_If) {
|
||||
} else if (stat instanceof AST_ForIn) {
|
||||
stat.object = cons_seq(stat.object);
|
||||
} else if (stat instanceof AST_If) {
|
||||
stat.condition = cons_seq(stat.condition);
|
||||
}
|
||||
else if (stat instanceof AST_With) {
|
||||
} else if (stat instanceof AST_Switch) {
|
||||
stat.expression = cons_seq(stat.expression);
|
||||
}
|
||||
else if (stat instanceof AST_Exit && stat.value) {
|
||||
stat.value = cons_seq(stat.value);
|
||||
}
|
||||
else if (stat instanceof AST_Exit) {
|
||||
stat.value = cons_seq(make_node(AST_Undefined, stat).transform(compressor));
|
||||
}
|
||||
else if (stat instanceof AST_Switch) {
|
||||
} else if (stat instanceof AST_With) {
|
||||
stat.expression = cons_seq(stat.expression);
|
||||
}
|
||||
}
|
||||
@@ -1712,6 +1736,7 @@ merge(Compressor.prototype, {
|
||||
do {
|
||||
var node = exprs[0];
|
||||
if (!(node instanceof AST_Assign)) break;
|
||||
if (node.operator != "=") break;
|
||||
if (!(node.left instanceof AST_PropAccess)) break;
|
||||
var sym = node.left.expression;
|
||||
if (!(sym instanceof AST_SymbolRef)) break;
|
||||
@@ -1722,6 +1747,12 @@ merge(Compressor.prototype, {
|
||||
prop = prop.evaluate(compressor);
|
||||
}
|
||||
if (prop instanceof AST_Node) break;
|
||||
prop = "" + prop;
|
||||
if (compressor.has_directive("use strict")) {
|
||||
if (!all(def.value.properties, function(node) {
|
||||
return node.key != prop && node.key.name != prop;
|
||||
})) break;
|
||||
}
|
||||
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
|
||||
key: prop,
|
||||
value: node.right
|
||||
@@ -1732,7 +1763,7 @@ merge(Compressor.prototype, {
|
||||
return trimmed && exprs;
|
||||
}
|
||||
|
||||
function join_consecutive_vars(statements, compressor) {
|
||||
function join_consecutive_vars(statements) {
|
||||
var defs;
|
||||
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
|
||||
var stat = statements[i];
|
||||
@@ -1748,8 +1779,15 @@ merge(Compressor.prototype, {
|
||||
statements[++j] = stat;
|
||||
defs = stat;
|
||||
}
|
||||
} else if (stat instanceof AST_Exit) {
|
||||
stat.value = extract_object_assignments(stat.value);
|
||||
} else if (stat instanceof AST_For) {
|
||||
if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
|
||||
var exprs = join_object_assignments(prev, stat.init);
|
||||
if (exprs) {
|
||||
CHANGED = true;
|
||||
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
|
||||
statements[++j] = stat;
|
||||
} else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
|
||||
if (stat.init) {
|
||||
prev.definitions = prev.definitions.concat(stat.init.definitions);
|
||||
}
|
||||
@@ -1764,6 +1802,10 @@ merge(Compressor.prototype, {
|
||||
} else {
|
||||
statements[++j] = stat;
|
||||
}
|
||||
} else if (stat instanceof AST_ForIn) {
|
||||
stat.object = extract_object_assignments(stat.object);
|
||||
} else if (stat instanceof AST_If) {
|
||||
stat.condition = extract_object_assignments(stat.condition);
|
||||
} else if (stat instanceof AST_SimpleStatement) {
|
||||
var exprs = join_object_assignments(prev, stat.body);
|
||||
if (exprs) {
|
||||
@@ -1772,11 +1814,31 @@ merge(Compressor.prototype, {
|
||||
stat.body = make_sequence(stat.body, exprs);
|
||||
}
|
||||
statements[++j] = stat;
|
||||
} else if (stat instanceof AST_Switch) {
|
||||
stat.expression = extract_object_assignments(stat.expression);
|
||||
} else if (stat instanceof AST_With) {
|
||||
stat.expression = extract_object_assignments(stat.expression);
|
||||
} else {
|
||||
statements[++j] = stat;
|
||||
}
|
||||
}
|
||||
statements.length = j + 1;
|
||||
|
||||
function extract_object_assignments(value) {
|
||||
statements[++j] = stat;
|
||||
var exprs = join_object_assignments(prev, value);
|
||||
if (exprs) {
|
||||
CHANGED = true;
|
||||
if (exprs.length) {
|
||||
return make_sequence(value, exprs);
|
||||
} else if (value instanceof AST_Sequence) {
|
||||
return value.tail_node().left;
|
||||
} else {
|
||||
return value.left;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1801,6 +1863,18 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
};
|
||||
|
||||
function get_value(key) {
|
||||
if (key instanceof AST_Constant) {
|
||||
return key.getValue();
|
||||
}
|
||||
if (key instanceof AST_UnaryPrefix
|
||||
&& key.operator == "void"
|
||||
&& key.expression instanceof AST_Constant) {
|
||||
return;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
function is_undefined(node, compressor) {
|
||||
return node.is_undefined
|
||||
|| node instanceof AST_Undefined
|
||||
@@ -2040,6 +2114,95 @@ merge(Compressor.prototype, {
|
||||
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
|
||||
}
|
||||
|
||||
function convert_to_predicate(obj) {
|
||||
for (var key in obj) {
|
||||
obj[key] = makePredicate(obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
var object_fns = [
|
||||
"constructor",
|
||||
"toString",
|
||||
"valueOf",
|
||||
];
|
||||
var native_fns = {
|
||||
Array: [
|
||||
"indexOf",
|
||||
"join",
|
||||
"lastIndexOf",
|
||||
"slice",
|
||||
].concat(object_fns),
|
||||
Boolean: object_fns,
|
||||
Number: [
|
||||
"toExponential",
|
||||
"toFixed",
|
||||
"toPrecision",
|
||||
].concat(object_fns),
|
||||
Object: object_fns,
|
||||
RegExp: [
|
||||
"test",
|
||||
].concat(object_fns),
|
||||
String: [
|
||||
"charAt",
|
||||
"charCodeAt",
|
||||
"concat",
|
||||
"indexOf",
|
||||
"italics",
|
||||
"lastIndexOf",
|
||||
"match",
|
||||
"replace",
|
||||
"search",
|
||||
"slice",
|
||||
"split",
|
||||
"substr",
|
||||
"substring",
|
||||
"trim",
|
||||
].concat(object_fns),
|
||||
};
|
||||
convert_to_predicate(native_fns);
|
||||
var static_fns = {
|
||||
Array: [
|
||||
"isArray",
|
||||
],
|
||||
Math: [
|
||||
"abs",
|
||||
"acos",
|
||||
"asin",
|
||||
"atan",
|
||||
"ceil",
|
||||
"cos",
|
||||
"exp",
|
||||
"floor",
|
||||
"log",
|
||||
"round",
|
||||
"sin",
|
||||
"sqrt",
|
||||
"tan",
|
||||
"atan2",
|
||||
"pow",
|
||||
"max",
|
||||
"min",
|
||||
],
|
||||
Number: [
|
||||
"isFinite",
|
||||
"isNaN",
|
||||
],
|
||||
Object: [
|
||||
"create",
|
||||
"getOwnPropertyDescriptor",
|
||||
"getOwnPropertyNames",
|
||||
"getPrototypeOf",
|
||||
"isExtensible",
|
||||
"isFrozen",
|
||||
"isSealed",
|
||||
"keys",
|
||||
],
|
||||
String: [
|
||||
"fromCharCode",
|
||||
],
|
||||
};
|
||||
convert_to_predicate(static_fns);
|
||||
|
||||
// methods to evaluate a constant expression
|
||||
(function(def){
|
||||
// If the node has been successfully reduced to a constant,
|
||||
@@ -2077,7 +2240,10 @@ merge(Compressor.prototype, {
|
||||
var elements = [];
|
||||
for (var i = 0, len = this.elements.length; i < len; i++) {
|
||||
var element = this.elements[i];
|
||||
if (element instanceof AST_Function) continue;
|
||||
if (element instanceof AST_Function) {
|
||||
elements.push(element);
|
||||
continue;
|
||||
}
|
||||
var value = element._eval(compressor, depth);
|
||||
if (element === value) return this;
|
||||
elements.push(value);
|
||||
@@ -2205,13 +2371,9 @@ merge(Compressor.prototype, {
|
||||
Array: Array,
|
||||
Math: Math,
|
||||
Number: Number,
|
||||
Object: Object,
|
||||
String: String,
|
||||
};
|
||||
function convert_to_predicate(obj) {
|
||||
for (var key in obj) {
|
||||
obj[key] = makePredicate(obj[key]);
|
||||
}
|
||||
}
|
||||
var static_values = {
|
||||
Math: [
|
||||
"E",
|
||||
@@ -2252,77 +2414,6 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return this;
|
||||
});
|
||||
var object_fns = [
|
||||
"constructor",
|
||||
"toString",
|
||||
"valueOf",
|
||||
];
|
||||
var native_fns = {
|
||||
Array: [
|
||||
"indexOf",
|
||||
"join",
|
||||
"lastIndexOf",
|
||||
"slice",
|
||||
].concat(object_fns),
|
||||
Boolean: object_fns,
|
||||
Number: [
|
||||
"toExponential",
|
||||
"toFixed",
|
||||
"toPrecision",
|
||||
].concat(object_fns),
|
||||
RegExp: [
|
||||
"test",
|
||||
].concat(object_fns),
|
||||
String: [
|
||||
"charAt",
|
||||
"charCodeAt",
|
||||
"concat",
|
||||
"indexOf",
|
||||
"italics",
|
||||
"lastIndexOf",
|
||||
"match",
|
||||
"replace",
|
||||
"search",
|
||||
"slice",
|
||||
"split",
|
||||
"substr",
|
||||
"substring",
|
||||
"trim",
|
||||
].concat(object_fns),
|
||||
};
|
||||
convert_to_predicate(native_fns);
|
||||
var static_fns = {
|
||||
Array: [
|
||||
"isArray",
|
||||
],
|
||||
Math: [
|
||||
"abs",
|
||||
"acos",
|
||||
"asin",
|
||||
"atan",
|
||||
"ceil",
|
||||
"cos",
|
||||
"exp",
|
||||
"floor",
|
||||
"log",
|
||||
"round",
|
||||
"sin",
|
||||
"sqrt",
|
||||
"tan",
|
||||
"atan2",
|
||||
"pow",
|
||||
"max",
|
||||
"min"
|
||||
],
|
||||
Number: [
|
||||
"isFinite",
|
||||
"isNaN",
|
||||
],
|
||||
String: [
|
||||
"fromCharCode",
|
||||
],
|
||||
};
|
||||
convert_to_predicate(static_fns);
|
||||
def(AST_Call, function(compressor, depth) {
|
||||
var exp = this.expression;
|
||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||
@@ -2347,7 +2438,16 @@ merge(Compressor.prototype, {
|
||||
if (arg === value) return this;
|
||||
args.push(value);
|
||||
}
|
||||
return val[key].apply(val, args);
|
||||
try {
|
||||
return val[key].apply(val, args);
|
||||
} catch (ex) {
|
||||
compressor.warn("Error evaluating {code} [{file}:{line},{col}]", {
|
||||
code: this.print_to_string(),
|
||||
file: this.start.file,
|
||||
line: this.start.line,
|
||||
col: this.start.col
|
||||
});
|
||||
}
|
||||
}
|
||||
return this;
|
||||
});
|
||||
@@ -2438,9 +2538,34 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("unsafe")) {
|
||||
var expr = this.expression;
|
||||
if (is_undeclared_ref(expr) && global_pure_fns(expr.name)) return true;
|
||||
if (expr instanceof AST_Dot
|
||||
&& is_undeclared_ref(expr.expression)
|
||||
&& (static_fns[expr.expression.name] || return_false)(expr.property)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.pure || !compressor.pure_funcs(this);
|
||||
});
|
||||
AST_Node.DEFMETHOD("is_call_pure", return_false);
|
||||
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
|
||||
if (!compressor.option("unsafe")) return;
|
||||
var expr = this.expression;
|
||||
var fns = return_false;
|
||||
if (expr instanceof AST_Array) {
|
||||
fns = native_fns.Array;
|
||||
} else if (expr.is_boolean()) {
|
||||
fns = native_fns.Boolean;
|
||||
} else if (expr.is_number(compressor)) {
|
||||
fns = native_fns.Number;
|
||||
} else if (expr instanceof AST_RegExp) {
|
||||
fns = native_fns.RegExp;
|
||||
} else if (expr.is_string(compressor)) {
|
||||
fns = native_fns.String;
|
||||
} else if (!this.may_throw_on_access(compressor)) {
|
||||
fns = native_fns.Object;
|
||||
}
|
||||
return fns(this.property);
|
||||
});
|
||||
|
||||
// determine if expression has side effects
|
||||
(function(def){
|
||||
@@ -2461,8 +2586,12 @@ merge(Compressor.prototype, {
|
||||
return any(this.body, compressor);
|
||||
});
|
||||
def(AST_Call, function(compressor){
|
||||
return !this.is_expr_pure(compressor)
|
||||
|| any(this.args, compressor);
|
||||
if (!this.is_expr_pure(compressor)
|
||||
&& (!this.expression.is_call_pure(compressor)
|
||||
|| this.expression.has_side_effects(compressor))) {
|
||||
return true;
|
||||
}
|
||||
return any(this.args, compressor);
|
||||
});
|
||||
def(AST_Switch, function(compressor){
|
||||
return this.expression.has_side_effects(compressor)
|
||||
@@ -2909,9 +3038,11 @@ merge(Compressor.prototype, {
|
||||
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
|
||||
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
if (def.value) {
|
||||
var ref = make_node(AST_SymbolRef, def.name, def.name);
|
||||
sym.references.push(ref);
|
||||
var assign = make_node(AST_Assign, def, {
|
||||
operator: "=",
|
||||
left: make_node(AST_SymbolRef, def.name, def.name),
|
||||
left: ref,
|
||||
right: def.value
|
||||
});
|
||||
if (fixed_ids[sym.id] === def) {
|
||||
@@ -3241,9 +3372,7 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
|
||||
var defs = defs_by_id[node.expression.definition().id];
|
||||
if (defs) {
|
||||
var key = node.property;
|
||||
if (key instanceof AST_Node) key = key.getValue();
|
||||
var def = defs.get(key);
|
||||
var def = defs.get(get_value(node.property));
|
||||
var sym = make_node(AST_SymbolRef, node, {
|
||||
name: def.name,
|
||||
scope: node.expression.scope,
|
||||
@@ -3294,6 +3423,12 @@ merge(Compressor.prototype, {
|
||||
def(AST_This, return_null);
|
||||
def(AST_Call, function(compressor, first_in_statement){
|
||||
if (!this.is_expr_pure(compressor)) {
|
||||
if (this.expression.is_call_pure(compressor)) {
|
||||
var exprs = this.args.slice();
|
||||
exprs.unshift(this.expression.expression);
|
||||
exprs = trim(exprs, compressor, first_in_statement);
|
||||
return exprs && make_sequence(this, exprs);
|
||||
}
|
||||
if (this.expression instanceof AST_Function
|
||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||
var node = this.clone();
|
||||
@@ -3336,8 +3471,10 @@ merge(Compressor.prototype, {
|
||||
while (left instanceof AST_PropAccess) {
|
||||
left = left.expression;
|
||||
}
|
||||
if (left instanceof AST_Symbol) return this;
|
||||
return this.right.drop_side_effect_free(compressor);
|
||||
if (left.is_constant_expression(compressor.find_parent(AST_Scope))) {
|
||||
return this.right.drop_side_effect_free(compressor);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
def(AST_Conditional, function(compressor){
|
||||
var consequent = this.consequent.drop_side_effect_free(compressor);
|
||||
@@ -3971,11 +4108,13 @@ merge(Compressor.prototype, {
|
||||
break;
|
||||
} else if (exp instanceof AST_Dot) switch(exp.property) {
|
||||
case "toString":
|
||||
if (self.args.length == 0) return make_node(AST_Binary, self, {
|
||||
left: make_node(AST_String, self, { value: "" }),
|
||||
operator: "+",
|
||||
right: exp.expression
|
||||
}).optimize(compressor);
|
||||
if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) {
|
||||
return make_node(AST_Binary, self, {
|
||||
left: make_node(AST_String, self, { value: "" }),
|
||||
operator: "+",
|
||||
right: exp.expression
|
||||
}).optimize(compressor);
|
||||
}
|
||||
break;
|
||||
case "join":
|
||||
if (exp.expression instanceof AST_Array) EXIT: {
|
||||
@@ -4083,7 +4222,7 @@ merge(Compressor.prototype, {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (compressor.option("unsafe_Func")
|
||||
if (compressor.option("unsafe_Function")
|
||||
&& is_undeclared_ref(exp)
|
||||
&& exp.name == "Function") {
|
||||
// new Function() => function(){}
|
||||
@@ -5003,6 +5142,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (single_use && fixed) {
|
||||
if (fixed instanceof AST_Defun) {
|
||||
fixed._squeezed = true;
|
||||
fixed = make_node(AST_Function, fixed, fixed);
|
||||
}
|
||||
var value;
|
||||
@@ -5090,7 +5230,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
OPT(AST_Undefined, function(self, compressor){
|
||||
if (compressor.option("unsafe")) {
|
||||
if (compressor.option("unsafe_undefined")) {
|
||||
var undef = find_variable(compressor, "undefined");
|
||||
if (undef) {
|
||||
var ref = make_node(AST_SymbolRef, self, {
|
||||
@@ -5589,6 +5729,7 @@ merge(Compressor.prototype, {
|
||||
if (def) {
|
||||
return def.optimize(compressor);
|
||||
}
|
||||
if (is_lhs(self, compressor.parent())) return self;
|
||||
if (compressor.option("unsafe_proto")
|
||||
&& self.expression instanceof AST_Dot
|
||||
&& self.expression.property == "prototype") {
|
||||
@@ -5627,7 +5768,6 @@ merge(Compressor.prototype, {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_lhs(self, compressor.parent())) return self;
|
||||
var sub = self.flatten_object(self.property, compressor);
|
||||
if (sub) return sub.optimize(compressor);
|
||||
var ev = self.evaluate(compressor);
|
||||
|
||||
@@ -451,6 +451,11 @@ function OutputStream(options) {
|
||||
return OUTPUT;
|
||||
};
|
||||
|
||||
function has_nlb() {
|
||||
var index = OUTPUT.lastIndexOf("\n");
|
||||
return /^ *$/.test(OUTPUT.slice(index + 1));
|
||||
}
|
||||
|
||||
function prepend_comments(node) {
|
||||
var self = this;
|
||||
var start = node.start;
|
||||
@@ -499,7 +504,7 @@ function OutputStream(options) {
|
||||
|
||||
comments = comments.filter(comment_filter, node);
|
||||
if (comments.length == 0) return;
|
||||
var last_nlb = /(^|\n) *$/.test(OUTPUT);
|
||||
var last_nlb = has_nlb();
|
||||
comments.forEach(function(c, i) {
|
||||
if (!last_nlb) {
|
||||
if (c.nlb) {
|
||||
@@ -546,7 +551,7 @@ function OutputStream(options) {
|
||||
print("\n");
|
||||
indent();
|
||||
need_newline_indented = false;
|
||||
} else if (c.nlb && (i > 0 || !/(^|\n) *$/.test(OUTPUT))) {
|
||||
} else if (c.nlb && (i > 0 || !has_nlb())) {
|
||||
print("\n");
|
||||
indent();
|
||||
} else if (i > 0 || !tail) {
|
||||
@@ -1344,11 +1349,8 @@ function OutputStream(options) {
|
||||
|
||||
function print_property_name(key, quote, output) {
|
||||
if (output.option("quote_keys")) {
|
||||
output.print_string(key + "");
|
||||
} else if ((typeof key == "number"
|
||||
|| !output.option("beautify")
|
||||
&& +key + "" == key)
|
||||
&& parseFloat(key) >= 0) {
|
||||
output.print_string(key);
|
||||
} else if ("" + +key == key && key >= 0) {
|
||||
output.print(make_num(key));
|
||||
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) {
|
||||
if (quote && output.option("keep_quoted_props")) {
|
||||
|
||||
@@ -1054,12 +1054,10 @@ function parse($TEXT, options) {
|
||||
};
|
||||
|
||||
function for_in(init) {
|
||||
var lhs = init instanceof AST_Var ? init.definitions[0].name : null;
|
||||
var obj = expression(true);
|
||||
expect(")");
|
||||
return new AST_ForIn({
|
||||
init : init,
|
||||
name : lhs,
|
||||
object : obj,
|
||||
body : in_loop(statement)
|
||||
});
|
||||
@@ -1367,7 +1365,7 @@ function parse($TEXT, options) {
|
||||
if (type == "name" && !is("punc", ":")) {
|
||||
var key = new AST_SymbolAccessor({
|
||||
start: S.token,
|
||||
name: as_property_name(),
|
||||
name: "" + as_property_name(),
|
||||
end: prev()
|
||||
});
|
||||
if (name == "get") {
|
||||
@@ -1393,7 +1391,7 @@ function parse($TEXT, options) {
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start : start,
|
||||
quote : start.quote,
|
||||
key : name,
|
||||
key : "" + name,
|
||||
value : expression(false),
|
||||
end : prev()
|
||||
}));
|
||||
|
||||
@@ -57,7 +57,7 @@ function SymbolDef(scope, orig, init) {
|
||||
this.id = SymbolDef.next_id++;
|
||||
};
|
||||
|
||||
SymbolDef.next_id = 1e6;
|
||||
SymbolDef.next_id = 1;
|
||||
|
||||
SymbolDef.prototype = {
|
||||
unmangleable: function(options) {
|
||||
@@ -305,7 +305,7 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
|
||||
|
||||
AST_Scope.DEFMETHOD("def_function", function(symbol, init){
|
||||
var def = this.def_variable(symbol, init);
|
||||
if (!def.init) def.init = init;
|
||||
if (!def.init || def.init instanceof AST_Defun) def.init = init;
|
||||
this.functions.set(symbol.name, def);
|
||||
return def;
|
||||
});
|
||||
|
||||
@@ -303,6 +303,13 @@ Dictionary.prototype = {
|
||||
ret.push(f(this._values[i], i.substr(1)));
|
||||
return ret;
|
||||
},
|
||||
clone: function() {
|
||||
var ret = new Dictionary();
|
||||
for (var i in this._values)
|
||||
ret._values[i] = this._values[i];
|
||||
ret._size = this._size;
|
||||
return ret;
|
||||
},
|
||||
toObject: function() { return this._values }
|
||||
};
|
||||
Dictionary.fromObject = function(obj) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.3.6",
|
||||
"version": "3.3.9",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -4012,3 +4012,103 @@ replace_all_var: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
cascade_statement: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
function f1(a, b) {
|
||||
var c;
|
||||
if (a)
|
||||
return c = b, c || a;
|
||||
else
|
||||
c = a, c(b);
|
||||
}
|
||||
function f2(a, b) {
|
||||
var c;
|
||||
while (a)
|
||||
c = b, a = c + b;
|
||||
do
|
||||
throw c = a + b, c;
|
||||
while (c);
|
||||
}
|
||||
function f3(a, b) {
|
||||
for (; a < b; a++)
|
||||
if (c = a, c && b)
|
||||
var c = (c = b(a), c);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f1(a, b) {
|
||||
var c;
|
||||
if (a)
|
||||
return (c = b) || a;
|
||||
else
|
||||
(c = a)(b);
|
||||
}
|
||||
function f2(a, b) {
|
||||
var c;
|
||||
while (a)
|
||||
a = (c = b) + b;
|
||||
do
|
||||
throw c = a + b;
|
||||
while (c);
|
||||
}
|
||||
function f3(a, b) {
|
||||
for (; a < b; a++)
|
||||
if ((c = a) && b)
|
||||
var c = c = b(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cascade_forin: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
function f(b) {
|
||||
return [ b, b, b ];
|
||||
}
|
||||
for (var c in a = console, f(a))
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
function f(b) {
|
||||
return [ b, b, b ];
|
||||
}
|
||||
for (var c in f(a = console))
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: [
|
||||
"0",
|
||||
"1",
|
||||
"2",
|
||||
]
|
||||
}
|
||||
|
||||
unsafe_builtin: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
var b = Math.abs(a);
|
||||
return Math.pow(b, 2);
|
||||
}
|
||||
console.log(f(-1), f(2));
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
return Math.pow(Math.abs(a), 2);
|
||||
}
|
||||
console.log(f(-1), f(2));
|
||||
}
|
||||
expect_stdout: "1 4"
|
||||
}
|
||||
|
||||
@@ -862,3 +862,20 @@ issue_2749: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
unsafe_builtin: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
(!w).constructor(x);
|
||||
Math.abs(y);
|
||||
[ 1, 2, z ].valueOf();
|
||||
}
|
||||
expect: {
|
||||
w, x;
|
||||
y;
|
||||
z;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1692,3 +1692,30 @@ issue_2768: {
|
||||
}
|
||||
expect_stdout: "PASS undefined"
|
||||
}
|
||||
|
||||
issue_2846: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b) {
|
||||
var a = 0;
|
||||
b && b(a);
|
||||
return a++;
|
||||
}
|
||||
var c = f();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = function(a, b) {
|
||||
a = 0;
|
||||
b && b(a);
|
||||
return a++;
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
@@ -1194,6 +1194,9 @@ issue_2231_1: {
|
||||
console.log(Object.keys(void 0));
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_warnings: [
|
||||
"WARN: Error evaluating Object.keys(void 0) [test/compress/evaluate.js:1191,20]",
|
||||
]
|
||||
}
|
||||
|
||||
issue_2231_2: {
|
||||
@@ -1208,6 +1211,23 @@ issue_2231_2: {
|
||||
console.log(Object.getOwnPropertyNames(null));
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_warnings: [
|
||||
"WARN: Error evaluating Object.getOwnPropertyNames(null) [test/compress/evaluate.js:1208,20]",
|
||||
]
|
||||
}
|
||||
|
||||
issue_2231_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(Object.keys({ foo: "bar" })[0]);
|
||||
}
|
||||
expect: {
|
||||
console.log("foo");
|
||||
}
|
||||
expect_stdout: "foo"
|
||||
}
|
||||
|
||||
self_comparison_1: {
|
||||
@@ -1330,13 +1350,27 @@ issue_2535_3: {
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_warnings: [
|
||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1316,20]",
|
||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1317,20]",
|
||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1318,20]",
|
||||
"WARN: Condition left of && always false [test/compress/evaluate.js:1318,20]",
|
||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1319,20]",
|
||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1320,20]",
|
||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1321,20]",
|
||||
"WARN: Condition left of || always true [test/compress/evaluate.js:1321,20]",
|
||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1336,20]",
|
||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1337,20]",
|
||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1338,20]",
|
||||
"WARN: Condition left of && always false [test/compress/evaluate.js:1338,20]",
|
||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1339,20]",
|
||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1340,20]",
|
||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1341,20]",
|
||||
"WARN: Condition left of || always true [test/compress/evaluate.js:1341,20]",
|
||||
]
|
||||
}
|
||||
|
||||
issue_2822: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log([ function() {}, "PASS", "FAIL" ][1]);
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -218,7 +218,7 @@ issue_203: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
side_effects: true,
|
||||
unsafe_Func: true,
|
||||
unsafe_Function: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
@@ -1951,3 +1951,41 @@ issue_2737_2: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2783: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
return g;
|
||||
function f(a) {
|
||||
var b = a.b;
|
||||
if (b) return b;
|
||||
return a;
|
||||
}
|
||||
function g(o, i) {
|
||||
while (i--) {
|
||||
console.log(f(o));
|
||||
}
|
||||
}
|
||||
})()({ b: "PASS" }, 1);
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
return function(o,i) {
|
||||
while (i--) console.log(f(o));
|
||||
};
|
||||
function f(a) {
|
||||
var b = a.b;
|
||||
return b || a;
|
||||
}
|
||||
})()({ b: "PASS" },1);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -664,3 +664,25 @@ issue_2519: {
|
||||
}
|
||||
expect_stdout: "5.5"
|
||||
}
|
||||
|
||||
undefined_key: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
hoist_props: true,
|
||||
join_vars: true,
|
||||
passes: 4,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a, o = {};
|
||||
o[a] = 1;
|
||||
o.b = 2;
|
||||
console.log(o[a] + o.b);
|
||||
}
|
||||
expect: {
|
||||
console.log(3);
|
||||
}
|
||||
expect_stdout: "3"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ unsafe_undefined: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
unsafe: true
|
||||
unsafe_undefined: true,
|
||||
}
|
||||
mangle = {}
|
||||
input: {
|
||||
@@ -30,7 +30,7 @@ keep_fnames: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
unsafe: true
|
||||
unsafe_undefined: true,
|
||||
}
|
||||
mangle = {
|
||||
keep_fnames: true
|
||||
|
||||
@@ -61,7 +61,7 @@ unsafe_undefined: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
unsafe: true,
|
||||
unsafe_undefined: true,
|
||||
}
|
||||
mangle = {}
|
||||
input: {
|
||||
|
||||
@@ -84,12 +84,12 @@ numeric_literal: {
|
||||
' 0: 0,',
|
||||
' "-0": 1,',
|
||||
' 42: 2,',
|
||||
' "42": 3,',
|
||||
' 42: 3,',
|
||||
' 37: 4,',
|
||||
' o: 5,',
|
||||
' 1e42: 6,',
|
||||
' b: 7,',
|
||||
' "1e+42": 8',
|
||||
' 1e42: 8',
|
||||
'};',
|
||||
'',
|
||||
'console.log(obj[-0], obj[-""], obj["-0"]);',
|
||||
|
||||
@@ -583,6 +583,25 @@ native_prototype: {
|
||||
}
|
||||
}
|
||||
|
||||
native_prototype_lhs: {
|
||||
options = {
|
||||
unsafe_proto: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
Function.prototype.bar = "PASS";
|
||||
return function() {};
|
||||
}().bar);
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
Function.prototype.bar = "PASS";
|
||||
return function() {};
|
||||
}().bar);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
accessor_boolean: {
|
||||
input: {
|
||||
var a = 1;
|
||||
@@ -1188,3 +1207,384 @@ join_object_assignments_3: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
join_object_assignments_return_1: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3
|
||||
};
|
||||
return o.q = "foo";
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3,
|
||||
q: "foo"
|
||||
};
|
||||
return o.q;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "foo"
|
||||
}
|
||||
|
||||
join_object_assignments_return_2: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3
|
||||
};
|
||||
return o.q = /foo/,
|
||||
o.r = "bar";
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3,
|
||||
q: /foo/,
|
||||
r: "bar"
|
||||
};
|
||||
return o.r;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "bar"
|
||||
}
|
||||
|
||||
join_object_assignments_return_3: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3
|
||||
};
|
||||
return o.q = "foo",
|
||||
o.p += "",
|
||||
console.log(o.q),
|
||||
o.p;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3,
|
||||
q: "foo"
|
||||
};
|
||||
return o.p += "",
|
||||
console.log(o.q),
|
||||
o.p;
|
||||
}());
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"3",
|
||||
]
|
||||
}
|
||||
|
||||
join_object_assignments_for: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var o = {
|
||||
p: 3
|
||||
};
|
||||
for (o.q = "foo"; console.log(o.q););
|
||||
return o.p;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
for (var o = {
|
||||
p: 3,
|
||||
q: "foo"
|
||||
}; console.log(o.q););
|
||||
return o.p;
|
||||
}());
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"3",
|
||||
]
|
||||
}
|
||||
|
||||
join_object_assignments_if: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var o = {};
|
||||
if (o.a = "PASS") return o.a;
|
||||
}())
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var o = { a: "PASS" };
|
||||
if (o.a) return o.a;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
join_object_assignments_forin: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
var o = {};
|
||||
for (var a in o.a = "PASS", o)
|
||||
return o[a];
|
||||
}())
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
var o = { a: "PASS" };
|
||||
for (var a in o)
|
||||
return o[a];
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
join_object_assignments_negative: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[0] = 0;
|
||||
o[-0] = 1;
|
||||
o[-1] = 2;
|
||||
console.log(o[0], o[-0], o[-1]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
0: 0,
|
||||
0: 1,
|
||||
"-1": 2
|
||||
};
|
||||
console.log(o[0], o[-0], o[-1]);
|
||||
}
|
||||
expect_stdout: "1 1 2"
|
||||
}
|
||||
|
||||
join_object_assignments_NaN_1: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[NaN] = 1;
|
||||
o[0/0] = 2;
|
||||
console.log(o[NaN], o[NaN]);
|
||||
}
|
||||
expect: {
|
||||
var o = {};
|
||||
o[NaN] = 1;
|
||||
o[0/0] = 2;
|
||||
console.log(o[NaN], o[NaN]);
|
||||
}
|
||||
expect_stdout: "2 2"
|
||||
}
|
||||
|
||||
join_object_assignments_NaN_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[NaN] = 1;
|
||||
o[0/0] = 2;
|
||||
console.log(o[NaN], o[NaN]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
NaN: 1,
|
||||
NaN: 2
|
||||
};
|
||||
console.log(o.NaN, o.NaN);
|
||||
}
|
||||
expect_stdout: "2 2"
|
||||
}
|
||||
|
||||
join_object_assignments_null_0: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[null] = 1;
|
||||
console.log(o[null]);
|
||||
}
|
||||
expect: {
|
||||
var o = {};
|
||||
o[null] = 1;
|
||||
console.log(o[null]);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
join_object_assignments_null_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[null] = 1;
|
||||
console.log(o[null]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
null: 1
|
||||
};
|
||||
console.log(o.null);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
join_object_assignments_void_0: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[void 0] = 1;
|
||||
console.log(o[void 0]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
undefined: 1
|
||||
};
|
||||
console.log(o[void 0]);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
join_object_assignments_undefined_1: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[undefined] = 1;
|
||||
console.log(o[undefined]);
|
||||
}
|
||||
expect: {
|
||||
var o = {};
|
||||
o[void 0] = 1;
|
||||
console.log(o[void 0]);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
join_object_assignments_undefined_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[undefined] = 1;
|
||||
console.log(o[undefined]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
undefined : 1
|
||||
};
|
||||
console.log(o[void 0]);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
join_object_assignments_Infinity: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[Infinity] = 1;
|
||||
o[1/0] = 2;
|
||||
o[-Infinity] = 3;
|
||||
o[-1/0] = 4;
|
||||
console.log(o[Infinity], o[1/0], o[-Infinity], o[-1/0]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
Infinity: 1,
|
||||
Infinity: 2,
|
||||
"-Infinity": 3,
|
||||
"-Infinity": 4
|
||||
};
|
||||
console.log(o[1/0], o[1/0], o[-1/0], o[-1/0]);
|
||||
}
|
||||
expect_stdout: "2 2 4 4"
|
||||
}
|
||||
|
||||
join_object_assignments_regex: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
join_vars: true,
|
||||
properties: true,
|
||||
}
|
||||
input: {
|
||||
var o = {};
|
||||
o[/rx/] = 1;
|
||||
console.log(o[/rx/]);
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
"/rx/": 1
|
||||
};
|
||||
console.log(o[/rx/]);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_2816: {
|
||||
options = {
|
||||
join_vars: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
var o = {
|
||||
a: 1
|
||||
};
|
||||
o.b = 2;
|
||||
o.a = 3;
|
||||
o.c = 4;
|
||||
console.log(o.a, o.b, o.c);
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
var o = {
|
||||
a: 1,
|
||||
b: 2
|
||||
};
|
||||
o.a = 3;
|
||||
o.c = 4;
|
||||
console.log(o.a, o.b, o.c);
|
||||
}
|
||||
expect_stdout: "3 2 4"
|
||||
}
|
||||
|
||||
@@ -694,3 +694,30 @@ issue_2678: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2838: {
|
||||
options = {
|
||||
pure_getters: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
function f(a, b) {
|
||||
(a || b).c = "PASS";
|
||||
(function() {
|
||||
return f(a, b);
|
||||
}).prototype.foo = "bar";
|
||||
}
|
||||
var o = {};
|
||||
f(null, o);
|
||||
console.log(o.c);
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
(a || b).c = "PASS";
|
||||
}
|
||||
var o = {};
|
||||
f(null, o);
|
||||
console.log(o.c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -5302,3 +5302,84 @@ issue_2774: {
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
issue_2799_1: {
|
||||
options = {
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return f;
|
||||
function f(n) {
|
||||
function g(i) {
|
||||
return i && i + g(i - 1);
|
||||
}
|
||||
function h(j) {
|
||||
return g(j);
|
||||
}
|
||||
return h(n);
|
||||
}
|
||||
}()(5));
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return function(n) {
|
||||
return function(j) {
|
||||
return function g(i) {
|
||||
return i && i + g(i - 1);
|
||||
}(j);
|
||||
}(n);
|
||||
}
|
||||
}()(5));
|
||||
}
|
||||
expect_stdout: "15"
|
||||
}
|
||||
|
||||
issue_2799_2: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unsafe_proto: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
function foo() {
|
||||
Function.prototype.call.apply(console.log, [ null, "PASS" ]);
|
||||
}
|
||||
foo();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
(function() {
|
||||
(function() {}).call.apply(console.log, [ null, "PASS" ]);
|
||||
})();
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2836: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
return "FAIL";
|
||||
}
|
||||
console.log(f());
|
||||
function f() {
|
||||
return "PASS";
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return "PASS";
|
||||
}());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ unsafe_undefined: {
|
||||
if_return: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unsafe: true,
|
||||
unsafe_undefined: true,
|
||||
}
|
||||
input: {
|
||||
function f(undefined) {
|
||||
@@ -859,3 +859,21 @@ for_init_var: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
forin: {
|
||||
options = {
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
var o = [];
|
||||
o.push("PASS");
|
||||
for (var a in o)
|
||||
console.log(o[a]);
|
||||
}
|
||||
expect: {
|
||||
var o = [];
|
||||
for (var a in o.push("PASS"), o)
|
||||
console.log(o[a]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
6
test/input/rename/input.js
Normal file
6
test/input/rename/input.js
Normal file
@@ -0,0 +1,6 @@
|
||||
function f(x) {
|
||||
return g(x);
|
||||
function g(x) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,24 @@
|
||||
var Mocha = require('mocha'),
|
||||
fs = require('fs'),
|
||||
path = require('path');
|
||||
var fs = require("fs");
|
||||
var Mocha = require("mocha");
|
||||
var path = require("path");
|
||||
|
||||
// Instantiate a Mocha instance.
|
||||
var mocha = new Mocha({});
|
||||
// Instantiate a Mocha instance
|
||||
var mocha = new Mocha({
|
||||
timeout: 5000
|
||||
});
|
||||
var testDir = __dirname + "/mocha/";
|
||||
|
||||
var testDir = __dirname + '/mocha/';
|
||||
|
||||
// Add each .js file to the mocha instance
|
||||
fs.readdirSync(testDir).filter(function(file){
|
||||
// Only keep the .js files
|
||||
return file.substr(-3) === '.js';
|
||||
|
||||
}).forEach(function(file){
|
||||
mocha.addFile(
|
||||
path.join(testDir, file)
|
||||
);
|
||||
// Add each .js file to the Mocha instance
|
||||
fs.readdirSync(testDir).filter(function(file) {
|
||||
return /\.js$/.test(file);
|
||||
}).forEach(function(file) {
|
||||
mocha.addFile(path.join(testDir, file));
|
||||
});
|
||||
|
||||
module.exports = function() {
|
||||
mocha.run(function(failures) {
|
||||
if (failures !== 0) {
|
||||
process.on('exit', function () {
|
||||
process.exit(failures);
|
||||
});
|
||||
}
|
||||
if (failures) process.on("exit", function() {
|
||||
process.exit(failures);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -650,4 +650,36 @@ describe("bin/uglifyjs", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with explicit --rename", function(done) {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js --rename";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(a){return b(a);function b(c){return c}}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with explicit --no-rename", function(done) {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js -mc --no-rename";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with implicit --rename", function(done) {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js -mc";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(n){return n}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should work with implicit --no-rename", function(done) {
|
||||
var command = uglifyjscmd + " test/input/rename/input.js -c";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,24 +17,30 @@ function safe_log(arg, level) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
function strip_func_ids(text) {
|
||||
return text.toString().replace(/F[0-9]{6}N/g, "<F<>N>");
|
||||
}
|
||||
|
||||
var FUNC_TOSTRING = [
|
||||
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
|
||||
" f.toString = Function.prototype.toString;",
|
||||
" f.valueOf = Function.prototype.valueOf;",
|
||||
"});",
|
||||
"Function.prototype.toString = Function.prototype.valueOf = function() {",
|
||||
" var id = 100000;",
|
||||
" return function() {",
|
||||
' if (this === Array) return "[Function: Array]";',
|
||||
' if (this === Object) return "[Function: Object]";',
|
||||
" var i = this.name;",
|
||||
' if (typeof i != "number") {',
|
||||
" i = ++id;",
|
||||
" var n = this.name;",
|
||||
' if (!/^F[0-9]{6}N$/.test(n)) {',
|
||||
' n = "F" + ++id + "N";',
|
||||
].concat(Object.getOwnPropertyDescriptor(Function.prototype, "name").configurable ? [
|
||||
' Object.defineProperty(this, "name", {',
|
||||
" get: function() {",
|
||||
" return i;",
|
||||
" return n;",
|
||||
" }",
|
||||
" });",
|
||||
] : [], [
|
||||
" }",
|
||||
' return "[Function: " + i + "]";',
|
||||
' return "[Function: " + n + "]";',
|
||||
" }",
|
||||
"}();",
|
||||
'Object.defineProperty(Function.prototype, "valueOf", { enumerable: false });',
|
||||
@@ -77,7 +83,7 @@ exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expec
|
||||
expected = expected.message.slice(expected.message.lastIndexOf("\n") + 1);
|
||||
actual = actual.message.slice(actual.message.lastIndexOf("\n") + 1);
|
||||
}
|
||||
return expected == actual;
|
||||
return strip_func_ids(expected) == strip_func_ids(actual);
|
||||
} : function(expected, actual) {
|
||||
return typeof expected == typeof actual && expected.toString() == actual.toString();
|
||||
return typeof expected == typeof actual && strip_func_ids(expected) == strip_func_ids(actual);
|
||||
};
|
||||
|
||||
82
test/travis-ufuzz.js
Normal file
82
test/travis-ufuzz.js
Normal file
@@ -0,0 +1,82 @@
|
||||
"use strict";
|
||||
|
||||
var child_process = require("child_process");
|
||||
var https = require("https");
|
||||
var url = require("url");
|
||||
|
||||
var period = 45 * 60 * 1000;
|
||||
var wait = 2 * 60 * 1000;
|
||||
var ping = 5 * 60 * 1000;
|
||||
if (process.argv[2] == "run") {
|
||||
var endTime = Date.now() + period;
|
||||
for (var i = 0; i < 2; i++) spawn(endTime);
|
||||
} else if (process.argv.length > 2) {
|
||||
var token = process.argv[2];
|
||||
var branch = process.argv[3] || "v" + require("../package.json").version;
|
||||
var repository = encodeURIComponent(process.argv[4] || "mishoo/UglifyJS2");
|
||||
var concurrency = process.argv[5] || 1;
|
||||
(function request() {
|
||||
setTimeout(request, (period + wait) / concurrency);
|
||||
var options = url.parse("https://api.travis-ci.org/repo/" + repository + "/requests");
|
||||
options.method = "POST";
|
||||
options.headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Travis-API-Version": 3,
|
||||
"Authorization": "token " + token
|
||||
};
|
||||
https.request(options, function(res) {
|
||||
console.log("HTTP", res.statusCode);
|
||||
console.log(JSON.stringify(res.headers, null, 2));
|
||||
console.log();
|
||||
res.setEncoding("utf8");
|
||||
res.on("data", console.log);
|
||||
}).on("error", console.error).end(JSON.stringify({
|
||||
request: {
|
||||
message: "ufuzz testing (when idle)",
|
||||
branch: branch,
|
||||
config: {
|
||||
merge_mode: "replace",
|
||||
language: "node_js",
|
||||
node_js: "9",
|
||||
sudo: false,
|
||||
script: "node test/travis-ufuzz run"
|
||||
}
|
||||
}
|
||||
}));
|
||||
})();
|
||||
} else {
|
||||
console.log("Usage: test/travis-ufuzz.js <token> [branch] [repository] [concurrency]");
|
||||
}
|
||||
|
||||
function spawn(endTime) {
|
||||
var child = child_process.spawn("node", [
|
||||
"--max-old-space-size=2048",
|
||||
"test/ufuzz"
|
||||
], {
|
||||
stdio: [ "ignore", "pipe", "pipe" ]
|
||||
}).on("exit", respawn);
|
||||
var line = "";
|
||||
child.stdout.on("data", function(data) {
|
||||
line += data;
|
||||
});
|
||||
child.stderr.on("data", function() {
|
||||
process.exitCode = 1;
|
||||
}).pipe(process.stdout);
|
||||
var keepAlive = setInterval(function() {
|
||||
var end = line.lastIndexOf("\r");
|
||||
console.log(line.slice(line.lastIndexOf("\r", end - 1) + 1, end));
|
||||
line = line.slice(end + 1);
|
||||
}, ping);
|
||||
var timer = setTimeout(function() {
|
||||
clearInterval(keepAlive);
|
||||
child.removeListener("exit", respawn);
|
||||
child.kill();
|
||||
}, endTime - Date.now());
|
||||
|
||||
function respawn() {
|
||||
console.log(line);
|
||||
clearInterval(keepAlive);
|
||||
clearTimeout(timer);
|
||||
spawn(endTime);
|
||||
}
|
||||
}
|
||||
@@ -998,10 +998,11 @@ function log_suspects(minify_options, component) {
|
||||
if (typeof options != "object") options = {};
|
||||
var defs = default_options[component];
|
||||
var suspects = Object.keys(defs).filter(function(name) {
|
||||
if ((name in options ? options : defs)[name]) {
|
||||
var flip = name == "keep_fargs";
|
||||
if (flip ? name in options : (name in options ? options : defs)[name]) {
|
||||
var m = JSON.parse(JSON.stringify(minify_options));
|
||||
var o = JSON.parse(JSON.stringify(options));
|
||||
o[name] = false;
|
||||
o[name] = flip;
|
||||
m[component] = o;
|
||||
var result = UglifyJS.minify(original_code, m);
|
||||
if (result.error) {
|
||||
@@ -1022,6 +1023,24 @@ function log_suspects(minify_options, component) {
|
||||
}
|
||||
}
|
||||
|
||||
function log_rename(options) {
|
||||
if (!options.rename) return;
|
||||
var m = JSON.parse(JSON.stringify(options));
|
||||
m.rename = false;
|
||||
var result = UglifyJS.minify(original_code, m);
|
||||
if (result.error) {
|
||||
errorln("Error testing options.rename");
|
||||
errorln(result.error.stack);
|
||||
} else {
|
||||
var r = sandbox.run_code(result.code);
|
||||
if (sandbox.same_stdout(original_result, r)) {
|
||||
errorln("Suspicious options:");
|
||||
errorln(" rename");
|
||||
errorln();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function log(options) {
|
||||
if (!ok) errorln('\n\n\n\n\n\n!!!!!!!!!!\n\n\n');
|
||||
errorln("//=============================================================");
|
||||
@@ -1056,6 +1075,7 @@ function log(options) {
|
||||
errorln();
|
||||
if (!ok && typeof uglify_code == "string") {
|
||||
Object.keys(default_options).forEach(log_suspects.bind(null, options));
|
||||
log_rename(options);
|
||||
errorln("!!!!!! Failed... round " + round);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"output": {
|
||||
"beautify": true,
|
||||
"bracketize": true
|
||||
}
|
||||
},
|
||||
"rename": true
|
||||
},
|
||||
{
|
||||
"compress": false
|
||||
@@ -20,7 +21,13 @@
|
||||
{
|
||||
"compress": {
|
||||
"keep_fargs": false,
|
||||
"passes": 100
|
||||
"passes": 1e6,
|
||||
"sequences": 1e6,
|
||||
"unsafe": true,
|
||||
"unsafe_Function": true,
|
||||
"unsafe_math": true,
|
||||
"unsafe_proto": true,
|
||||
"unsafe_regexp": true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user