Compare commits

..

14 Commits

Author SHA1 Message Date
Alex Lam S.L
4377e932ca v3.0.15 2017-06-01 18:12:38 +08:00
Alex Lam S.L
bac14ba881 fix non-identifier getter/setter name (#2041)
fixes #2040
2017-06-01 18:11:16 +08:00
Alex Lam S.L
ec095ed647 whitelist unsafe evaluate candidates (#2039)
- all arguments may accept constant values
- return constant value
- free of side effects
- available & identical across locales and runtime environments
2017-06-01 04:33:05 +08:00
Alex Lam S.L
17e73121fa enhance unsafe evaluate (#2037) 2017-06-01 00:56:28 +08:00
kzc
f71e8fd948 reformat mangle options section of README (#2036) 2017-05-31 21:52:43 +08:00
Alex Lam S.L
3e62faa64f v3.0.14 2017-05-31 11:34:51 +08:00
Alex Lam S.L
e9645e017f introduce unsafe_Func (#2033)
Separate flag for #203 functionality.
2017-05-31 03:38:00 +08:00
Alex Lam S.L
55b5f2a8aa widen CLI parse error code fragment displayed (#2032)
fixes #2030
2017-05-31 01:56:52 +08:00
Alex Lam S.L
4e0a22e5c8 v3.0.13 2017-05-29 10:52:13 +08:00
Alex Lam S.L
1aa38051fb better fix for #512 & #2010 (#2019)
- remove duplicated functionalities
- fix similar issue with `else`
2017-05-29 10:51:41 +08:00
Alex Lam S.L
e62b879b48 display default values in --help options (#2018) 2017-05-28 22:57:20 +08:00
Alex Lam S.L
c6c9f4f5a8 implement --help options (#2017) 2017-05-28 18:21:44 +08:00
Alex Lam S.L
fec14379f6 improve CLI usability (#2016)
Report supported options upon invalid option syntax.

fixes #1883
2017-05-28 04:09:40 +08:00
Alex Lam S.L
79131cd647 extend node_version range on applicable tests (#2015) 2017-05-27 22:18:28 +08:00
15 changed files with 364 additions and 172 deletions

View File

@@ -45,6 +45,7 @@ a double dash to prevent input files being used as option arguments:
``` ```
-h, --help Print usage information. -h, --help Print usage information.
`--help options` for details on available options.
-V, --version Print version number. -V, --version Print version number.
-p, --parse <options> Specify parser options: -p, --parse <options> Specify parser options:
`acorn` Use Acorn for parsing. `acorn` Use Acorn for parsing.
@@ -571,6 +572,8 @@ 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 comparison are switching. Compression only works if both `comparisons` and
`unsafe_comps` are both set to true. `unsafe_comps` are both set to true.
- `unsafe_Func` (default: false) -- compress and mangle `Function(args, code)`.
- `unsafe_math` (default: false) -- optimize numerical expressions like - `unsafe_math` (default: false) -- optimize numerical expressions like
`2 * x * 3` into `6 * x`, which may give imprecise floating point results. `2 * x * 3` into `6 * x`, which may give imprecise floating point results.
@@ -677,17 +680,18 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
## Mangle options ## Mangle options
- `reserved` - pass an array of identifiers that should be excluded from mangling - `reserved` (default `[]`). Pass an array of identifiers that should be
excluded from mangling. Example: `["foo", "bar"]`.
- `toplevel` — mangle names declared in the top level scope (disabled by - `toplevel` (default `false`). Pass `true` to mangle names declared in the
default). top level scope.
- `eval` — mangle names visible in scopes where eval or with are used - `keep_fnames` (default `false`). Pass `true` to not mangle function names.
(disabled by default). Useful for code relying on `Function.prototype.name`. See also: the `keep_fnames`
[compress option](#compress-options).
- `keep_fnames` -- default `false`. Pass `true` to not mangle - `eval` (default `false`). Pass `true` to mangle names visible in scopes
function names. Useful for code relying on `Function.prototype.name`. where `eval` or `with` are used.
See also: the `keep_fnames` [compress option](#compress-options).
Examples: Examples:

View File

@@ -21,10 +21,27 @@ var options = {
compress: false, compress: false,
mangle: false mangle: false
}; };
program.version(info.name + ' ' + info.version); program.version(info.name + " " + info.version);
program.parseArgv = program.parse; program.parseArgv = program.parse;
program.parse = undefined; program.parse = undefined;
if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast; if (process.argv.indexOf("ast") >= 0) program.helpInformation = UglifyJS.describe_ast;
else if (process.argv.indexOf("options") >= 0) program.helpInformation = function() {
var text = [];
var options = UglifyJS.default_options();
for (var option in options) {
text.push("--" + (option == "output" ? "beautify" : option == "sourceMap" ? "source-map" : option) + " options:");
var defs = options[option];
var padding = "";
Object.keys(defs).map(function(name) {
if (padding.length < name.length) padding = Array(name.length + 1).join(" ");
return [ name, JSON.stringify(defs[name]) ];
}).forEach(function(tokens) {
text.push(" " + tokens[0] + padding.slice(tokens[0].length - 2) + tokens[1]);
});
text.push("");
}
return text.join("\n");
};
program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true)); program.option("-p, --parse <options>", "Specify parser options.", parse_js("parse", true));
program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true)); program.option("-c, --compress [options]", "Enable compressor/specify compressor options.", parse_js("compress", true));
program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true)); program.option("-m, --mangle [options]", "Mangle names/specify mangler options.", parse_js("mangle", true));
@@ -208,9 +225,10 @@ function run() {
col = line.length; col = line.length;
} }
if (line) { if (line) {
if (col > 40) { var limit = 70;
line = line.slice(col - 40); if (col > limit) {
col = 40; line = line.slice(col - limit);
col = limit;
} }
console.error(line.slice(0, 80)); console.error(line.slice(0, 80));
console.error(line.slice(0, col).replace(/\S/g, " ") + "^"); console.error(line.slice(0, col).replace(/\S/g, " ") + "^");
@@ -351,7 +369,7 @@ function parse_js(flag, constants) {
} }
})); }));
} catch(ex) { } catch(ex) {
fatal("Error parsing arguments for '" + flag + "': " + value); options[value] = null;
} }
return options; return options;
} }

View File

@@ -81,6 +81,7 @@ function Compressor(options, false_by_default) {
toplevel : !!(options && options["top_retain"]), toplevel : !!(options && options["top_retain"]),
unsafe : false, unsafe : false,
unsafe_comps : false, unsafe_comps : false,
unsafe_Func : false,
unsafe_math : false, unsafe_math : false,
unsafe_proto : false, unsafe_proto : false,
unsafe_regexp : false, unsafe_regexp : false,
@@ -924,6 +925,45 @@ merge(Compressor.prototype, {
// step. nevertheless, it's good to check. // step. nevertheless, it's good to check.
continue loop; continue loop;
case stat instanceof AST_If: case stat instanceof AST_If:
var ab = aborts(stat.body);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
});
var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body
});
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
if (stat.body instanceof AST_Return) { if (stat.body instanceof AST_Return) {
var value = stat.body.value; var value = stat.body.value;
//--- //---
@@ -960,23 +1000,6 @@ merge(Compressor.prototype, {
continue loop; continue loop;
} }
//--- //---
// if (foo()) return [ void bar() ]; [ else x...; ] y... ==> if (!foo()) { x...; y... } else bar();
if (in_lambda && (!value || value instanceof AST_UnaryPrefix && value.operator == "void")) {
CHANGED = true;
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array(stat.alternative).concat(ret);
stat.body = make_node(AST_BlockStatement, stat, {
body: body
});
stat.alternative = value ? make_node(AST_SimpleStatement, value, {
body: value.expression
}) : null;
ret = [ stat.transform(compressor) ].concat(funs);
continue loop;
}
//---
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
// //
// if sequences is not enabled, this can lead to an endless loop (issue #866). // if sequences is not enabled, this can lead to an endless loop (issue #866).
@@ -995,48 +1018,6 @@ merge(Compressor.prototype, {
} }
} }
var ab = aborts(stat.body);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
var body = as_statement_array(stat.body).slice(0, -1);
stat = stat.clone();
stat.condition = stat.condition.negate(compressor);
stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat, {
body: body
});
ret = [ stat.transform(compressor) ];
continue loop;
}
var ab = aborts(stat.alternative);
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|| (ab instanceof AST_Continue && self === loop_body(lct))
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
if (ab.label) {
remove(ab.label.thedef.references, ab);
}
CHANGED = true;
stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret)
});
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: as_statement_array(stat.alternative).slice(0, -1)
});
ret = [ stat.transform(compressor) ];
continue loop;
}
ret.unshift(stat); ret.unshift(stat);
break; break;
default: default:
@@ -1056,6 +1037,28 @@ merge(Compressor.prototype, {
} }
return false; return false;
} }
function is_return_void(value) {
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
}
function can_merge_flow(ab) {
if (!ab) return false;
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct)
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
}
function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1);
if (ab.value) {
body.push(make_node(AST_SimpleStatement, ab.value, {
body: ab.value.expression
}));
}
return body;
}
}; };
function eliminate_dead_code(statements, compressor) { function eliminate_dead_code(statements, compressor) {
@@ -1662,6 +1665,63 @@ merge(Compressor.prototype, {
} }
throw def; throw def;
}); });
var object_fns = [
'constructor',
'toString',
'valueOf',
];
var native_fns = {
Array: makePredicate([
'indexOf',
'join',
'lastIndexOf',
'slice',
].concat(object_fns)),
Boolean: makePredicate(object_fns),
Number: makePredicate([
'toExponential',
'toFixed',
'toPrecision',
].concat(object_fns)),
RegExp: makePredicate([
'test',
].concat(object_fns)),
String: makePredicate([
'charAt',
'charCodeAt',
'concat',
'indexOf',
'italics',
'lastIndexOf',
'match',
'replace',
'search',
'slice',
'split',
'substr',
'substring',
'trim',
].concat(object_fns)),
};
def(AST_Call, function(compressor){
var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
var key = exp.property;
if (key instanceof AST_Node) {
key = ev(key, compressor);
}
var val = ev(exp.expression, compressor);
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
return val[key].apply(val, this.args.map(function(arg) {
return ev(arg, compressor);
}));
}
}
throw def;
});
def(AST_New, function(compressor){
throw def;
});
})(function(node, func){ })(function(node, func){
node.DEFMETHOD("_eval", func); node.DEFMETHOD("_eval", func);
}); });
@@ -2971,63 +3031,6 @@ merge(Compressor.prototype, {
operator: "!" operator: "!"
}).optimize(compressor); }).optimize(compressor);
break; break;
case "Function":
// new Function() => function(){}
if (self.args.length == 0) return make_node(AST_Function, self, {
argnames: [],
body: []
});
if (all(self.args, function(x){ return x instanceof AST_String })) {
// quite a corner-case, but we can handle it:
// https://github.com/mishoo/UglifyJS2/issues/203
// if the code argument is a constant, then we can minify it.
try {
var code = "(function(" + self.args.slice(0, -1).map(function(arg){
return arg.value;
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
var ast = parse(code);
var mangle = { ie8: compressor.option("ie8") };
ast.figure_out_scope(mangle);
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
ast.mangle_names();
var fun;
try {
ast.walk(new TreeWalker(function(node){
if (node instanceof AST_Lambda) {
fun = node;
throw ast;
}
}));
} catch(ex) {
if (ex !== ast) throw ex;
};
if (!fun) return self;
var args = fun.argnames.map(function(arg, i){
return make_node(AST_String, self.args[i], {
value: arg.print_to_string()
});
});
var code = OutputStream();
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
code = code.toString().replace(/^\{|\}$/g, "");
args.push(make_node(AST_String, self.args[self.args.length - 1], {
value: code
}));
self.args = args;
return self;
} catch(ex) {
if (ex instanceof JS_Parse_Error) {
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
compressor.warn(ex.toString());
} else {
console.log(ex);
throw ex;
}
}
}
break;
} }
} }
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) { else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
@@ -3110,6 +3113,63 @@ merge(Compressor.prototype, {
} }
} }
} }
if (compressor.option("unsafe_Func")
&& exp instanceof AST_SymbolRef
&& exp.undeclared()
&& exp.name == "Function") {
// new Function() => function(){}
if (self.args.length == 0) return make_node(AST_Function, self, {
argnames: [],
body: []
});
if (all(self.args, function(x) {
return x instanceof AST_String;
})) {
// quite a corner-case, but we can handle it:
// https://github.com/mishoo/UglifyJS2/issues/203
// if the code argument is a constant, then we can minify it.
try {
var code = "NaN(function(" + self.args.slice(0, -1).map(function(arg) {
return arg.value;
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
var ast = parse(code);
var mangle = { ie8: compressor.option("ie8") };
ast.figure_out_scope(mangle);
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
ast.mangle_names();
var fun;
ast.walk(new TreeWalker(function(node) {
if (fun) return true;
if (node instanceof AST_Lambda) {
fun = node;
return true;
}
}));
var args = fun.argnames.map(function(arg, i) {
return make_node(AST_String, self.args[i], {
value: arg.print_to_string()
});
});
var code = OutputStream();
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
code = code.toString().replace(/^\{|\}$/g, "");
args.push(make_node(AST_String, self.args[self.args.length - 1], {
value: code
}));
self.args = args;
return self;
} catch (ex) {
if (ex instanceof JS_Parse_Error) {
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
compressor.warn(ex.toString());
} else {
throw ex;
}
}
}
}
if (exp instanceof AST_Function) { if (exp instanceof AST_Function) {
if (exp.body[0] instanceof AST_Return) { if (exp.body[0] instanceof AST_Return) {
var value = exp.body[0].value; var value = exp.body[0].value;
@@ -3141,6 +3201,11 @@ merge(Compressor.prototype, {
&& is_iife_call(self)) { && is_iife_call(self)) {
return self.negate(compressor, true); return self.negate(compressor, true);
} }
var ev = self.evaluate(compressor);
if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor);
return best_of(compressor, ev, self);
}
return self; return self;
}); });

View File

@@ -1234,9 +1234,8 @@ function OutputStream(options) {
}); });
else output.print("{}"); else output.print("{}");
}); });
DEFPRINT(AST_ObjectKeyVal, function(self, output){
var key = self.key; function print_property_name(key, quote, output) {
var quote = self.quote;
if (output.option("quote_keys")) { if (output.option("quote_keys")) {
output.print_string(key + ""); output.print_string(key + "");
} else if ((typeof key == "number" } else if ((typeof key == "number"
@@ -1253,20 +1252,24 @@ function OutputStream(options) {
} else { } else {
output.print_string(key, quote); output.print_string(key, quote);
} }
}
DEFPRINT(AST_ObjectKeyVal, function(self, output){
print_property_name(self.key, self.quote, output);
output.colon(); output.colon();
self.value.print(output); self.value.print(output);
}); });
DEFPRINT(AST_ObjectSetter, function(self, output){ AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
output.print("set"); output.print(type);
output.space(); output.space();
self.key.print(output); print_property_name(this.key.name, this.quote, output);
self.value._do_print(output, true); this.value._do_print(output, true);
});
DEFPRINT(AST_ObjectSetter, function(self, output){
self._print_getter_setter("set", output);
}); });
DEFPRINT(AST_ObjectGetter, function(self, output){ DEFPRINT(AST_ObjectGetter, function(self, output){
output.print("get"); self._print_getter_setter("get", output);
output.space();
self.key.print(output);
self.value._do_print(output, true);
}); });
DEFPRINT(AST_Symbol, function(self, output){ DEFPRINT(AST_Symbol, function(self, output){
var def = self.definition(); var def = self.definition();

View File

@@ -700,7 +700,7 @@ function parse($TEXT, options) {
shebang : true, shebang : true,
strict : false, strict : false,
toplevel : null, toplevel : null,
}); }, true);
var S = { var S = {
input : (typeof $TEXT == "string" input : (typeof $TEXT == "string"

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.12", "version": "3.0.15",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -99,7 +99,7 @@ dead_code_2_should_warn_strict: {
f(); f();
} }
expect_stdout: true expect_stdout: true
node_version: "=4" node_version: ">=4"
} }
dead_code_constant_boolean_should_warn_more: { dead_code_constant_boolean_should_warn_more: {

View File

@@ -780,13 +780,15 @@ unsafe_charAt_noop: {
input: { input: {
console.log( console.log(
s.charAt(0), s.charAt(0),
"string".charAt(x) "string".charAt(x),
(typeof x).charAt()
); );
} }
expect: { expect: {
console.log( console.log(
s.charAt(0), s.charAt(0),
"string".charAt(x) "string".charAt(x),
(typeof x)[0]
); );
} }
} }
@@ -1037,3 +1039,31 @@ issue_1964_2: {
} }
expect_stdout: "b" expect_stdout: "b"
} }
array_slice_index: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log([1,2,3].slice(1)[1]);
}
expect: {
console.log(3);
}
expect_stdout: "3"
}
string_charCodeAt: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log("foo".charCodeAt("bar".length));
}
expect: {
console.log(NaN);
}
expect_stdout: "NaN"
}

View File

@@ -243,5 +243,27 @@ hoist_funs_strict: {
"5 'function' 'function'", "5 'function' 'function'",
"6 'undefined' 'function'", "6 'undefined' 'function'",
] ]
node_version: "=4" node_version: ">=4"
}
issue_203: {
options = {
keep_fargs: false,
side_effects: true,
unsafe_Func: true,
unused: true,
}
input: {
var m = {};
var fn = Function("require", "module", "exports", "module.exports = 42;");
fn(null, m, m.exports);
console.log(m.exports);
}
expect: {
var m = {};
var fn = Function("a", "b", "b.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
expect_stdout: "42"
} }

View File

@@ -307,6 +307,8 @@ issue_512: {
options = { options = {
conditionals: true, conditionals: true,
if_return: true, if_return: true,
sequences: true,
side_effects: true,
} }
input: { input: {
function a() { function a() {

View File

@@ -136,7 +136,29 @@ defun_hoist_funs: {
function f() {} function f() {}
function g() {} function g() {}
function h() {} function h() {}
!window; if (window);
}
}
}
defun_else_if_return: {
options = {
hoist_funs: false,
if_return: true,
}
input: {
function e() {
function f() {}
if (window) function g() {}
else return;
function h() {}
}
}
expect: {
function e() {
function f() {}
if (window) function g() {}
function h() {}
} }
} }
} }

View File

@@ -555,3 +555,20 @@ native_prototype: {
"".indexOf.call(e, "bar"); "".indexOf.call(e, "bar");
} }
} }
issue_2040: {
input: {
var a = 1;
var b = {
get "a-b"() {
return a;
},
set "a-b"(c) {
a = c;
}
};
console.log(b["a-b"], b["a-b"] = 2, b["a-b"]);
}
expect_exact: 'var a=1;var b={get"a-b"(){return a},set"a-b"(c){a=c}};console.log(b["a-b"],b["a-b"]=2,b["a-b"]);'
expect_stdout: "1 2 2"
}

View File

@@ -537,4 +537,13 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should print supported options on invalid option syntax", function(done) {
var command = uglifyjscmd + " test/input/comments/filter.js -b ascii-only";
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.ok(/^Supported options:\n\{[^}]+}\nERROR: `ascii-only` is not a supported option/.test(stderr), stderr);
done();
});
});
}); });

View File

@@ -12,7 +12,7 @@
stream._handle.setBlocking(true); stream._handle.setBlocking(true);
}); });
var UglifyJS = require("./node"); var UglifyJS = require("..");
var randomBytes = require("crypto").randomBytes; var randomBytes = require("crypto").randomBytes;
var sandbox = require("./sandbox"); var sandbox = require("./sandbox");
@@ -962,32 +962,15 @@ function try_beautify(code, result) {
console.log(code); console.log(code);
} }
function infer_options(ctor) { var default_options = UglifyJS.default_options();
try {
ctor({ 0: 0 });
} catch (e) {
return e.defs;
}
}
var default_options = {
compress: infer_options(UglifyJS.Compressor),
mangle: {
"cache": null,
"eval": false,
"ie8": false,
"keep_fnames": false,
"toplevel": false,
},
output: infer_options(UglifyJS.OutputStream),
};
function log_suspects(minify_options, component) { function log_suspects(minify_options, component) {
var options = component in minify_options ? minify_options[component] : true; var options = component in minify_options ? minify_options[component] : true;
if (!options) return; if (!options) return;
options = UglifyJS.defaults(options, default_options[component]); if (typeof options != "object") options = {};
var suspects = Object.keys(default_options[component]).filter(function(name) { var defs = default_options[component];
if (options[name]) { var suspects = Object.keys(defs).filter(function(name) {
if ((name in options ? options : defs)[name]) {
var m = JSON.parse(JSON.stringify(minify_options)); var m = JSON.parse(JSON.stringify(minify_options));
var o = JSON.parse(JSON.stringify(options)); var o = JSON.parse(JSON.stringify(options));
o[name] = false; o[name] = false;

View File

@@ -63,3 +63,20 @@ function describe_ast() {
doitem(AST_Node); doitem(AST_Node);
return out + "\n"; return out + "\n";
} }
function infer_options(options) {
var result = UglifyJS.minify("", options);
return result.error && result.error.defs;
}
UglifyJS.default_options = function() {
var defs = {};
Object.keys(infer_options({ 0: 0 })).forEach(function(component) {
var options = {};
options[component] = { 0: 0 };
if (options = infer_options(options)) {
defs[component] = options;
}
});
return defs;
};