Compare commits

...

19 Commits

Author SHA1 Message Date
Alex Lam S.L
2dde41615a v3.0.23 2017-07-02 17:24:22 +08:00
Alex Lam S.L
8b69a3d18e drop argument value after collapse_vars (#2190) 2017-07-02 04:28:11 +08:00
Alex Lam S.L
d40950b741 improve inline efficiency (#2188)
... by teaching `collapse_vars` some new tricks.

fixes #2187
2017-07-02 01:05:14 +08:00
Alex Lam S.L
7659ea1d2e v3.0.22 2017-06-30 11:18:34 +08:00
Alex Lam S.L
bdeadffbf5 improve usability of name cache under minify() (#2176)
fixes #2174
2017-06-29 12:48:34 +08:00
Alex Lam S.L
5e6f26445f v3.0.21 2017-06-29 00:49:06 +08:00
Alex Lam S.L
f0a99125ee improve unsafe_Func (#2171)
- minimise disturbance to `compute_char_frequency()`
- remove extraneous quotation marks
2017-06-27 23:53:42 +08:00
Alex Lam S.L
1e4de2e6d3 parse @global_defs as expressions (#2169)
- let parser rejects non-conformant input
- eliminate need for extraneous parenthesis
2017-06-27 10:31:19 +08:00
Alex Lam S.L
8b4dcd8f3e v3.0.20 2017-06-25 15:05:05 +08:00
Alex Lam S.L
285401ced8 more tests for #2158 (#2160) 2017-06-25 14:21:48 +08:00
Alex Lam S.L
9db4c42380 fix cascade & collapse on property access of constants (#2158) 2017-06-24 21:30:06 +08:00
Alex Lam S.L
94e5e00c03 refactor compute_char_frequency() (#2152)
- minimise maintenance when updating AST
- maximise code sharing between `master` & `harmony`
2017-06-23 20:05:31 +08:00
Alex Lam S.L
dc6bcaa18e synchronise mangle.properties for minify() & test/compress (#2151) 2017-06-23 15:53:13 +08:00
Alex Lam S.L
d58b184835 refactor Compressor.toplevel (#2149) 2017-06-23 13:11:40 +08:00
Alex Lam S.L
b3a57ff019 minimise reduce_vars cloning overhead (#2148) 2017-06-23 06:59:53 +08:00
Alex Lam S.L
3d5bc08185 fix reduce_vars on this (#2145)
fixes #2140
2017-06-23 04:44:57 +08:00
Alex Lam S.L
0692435f01 fix for-in loop parsing (#2144) 2017-06-23 04:14:30 +08:00
Alex Lam S.L
f67a6b0e43 v3.0.19 2017-06-22 03:24:22 +08:00
Alex Lam S.L
343ea326c2 ensure mangling works if catch reuses a scope variable (#2123)
fixes #2120
2017-06-20 02:14:05 +08:00
27 changed files with 823 additions and 298 deletions

View File

@@ -111,7 +111,7 @@ a double dash to prevent input files being used as option arguments:
By default UglifyJS will not try to be IE-proof.
--keep-fnames Do not mangle/drop function names. Useful for
code relying on Function.prototype.name.
--name-cache File to hold mangled name mappings.
--name-cache <file> File to hold mangled name mappings.
--self Build UglifyJS as a library (implies --wrap UglifyJS)
--source-map [options] Enable source map/specify source map options:
`base` Path to compute relative paths from input files.
@@ -383,7 +383,47 @@ var code = {
var options = { toplevel: true };
var result = UglifyJS.minify(code, options);
console.log(result.code);
// console.log(function(n,o){return n+o}(3,7));
// console.log(3+7);
```
The `nameCache` option:
```javascript
var options = {
mangle: {
toplevel: true,
},
nameCache: {}
};
var result1 = UglifyJS.minify({
"file1.js": "function add(first, second) { return first + second; }"
}, options);
var result2 = UglifyJS.minify({
"file2.js": "console.log(add(1 + 2, 3 + 4));"
}, options);
console.log(result1.code);
// function n(n,r){return n+r}
console.log(result2.code);
// console.log(n(3,7));
```
You may persist the name cache to the file system in the following way:
```javascript
var cacheFileName = "/tmp/cache.json";
var options = {
mangle: {
properties: true,
},
nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
};
fs.writeFileSync("part1.js", UglifyJS.minify({
"file1.js": fs.readFileSync("file1.js", "utf8"),
"file2.js": fs.readFileSync("file2.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync("part2.js", UglifyJS.minify({
"file3.js": fs.readFileSync("file3.js", "utf8"),
"file4.js": fs.readFileSync("file4.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");
```
An example of a combination of `minify()` options:
@@ -461,6 +501,13 @@ if (result.error) throw result.error;
- `toplevel` (default `false`) - set to `true` if you wish to enable top level
variable and function name mangling and to drop unused variables and functions.
- `nameCache` (default `null`) - pass an empty object `{}` or a previously
used `nameCache` object if you wish to cache mangled variable and
property names across multiple invocations of `minify()`. Note: this is
a read/write property. `minify()` will read the name cache state of this
object and update it during minification so that it may be
reused or externally persisted by the user.
- `ie8` (default `false`) - set to `true` to support IE8.
## Minify options structure
@@ -487,6 +534,7 @@ if (result.error) throw result.error;
sourceMap: {
// source map options
},
nameCache: null, // or specify a name cache object
toplevel: false,
ie8: false,
}

View File

@@ -106,17 +106,8 @@ if (program.mangleProps) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.properties = program.mangleProps;
}
var cache;
if (program.nameCache) {
cache = JSON.parse(read_file(program.nameCache, "{}"));
if (options.mangle) {
if (typeof options.mangle != "object") options.mangle = {};
options.mangle.cache = to_cache("vars");
if (options.mangle.properties) {
if (typeof options.mangle.properties != "object") options.mangle.properties = {};
options.mangle.properties.cache = to_cache("props");
}
}
options.nameCache = JSON.parse(read_file(program.nameCache, "{}"));
}
if (program.output == "ast") {
options.output = {
@@ -266,9 +257,7 @@ function run() {
print(result.code);
}
if (program.nameCache) {
fs.writeFileSync(program.nameCache, JSON.stringify(cache, function(key, value) {
return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
}));
fs.writeFileSync(program.nameCache, JSON.stringify(options.nameCache));
}
if (result.timings) for (var phase in result.timings) {
print_error("- " + phase + ": " + result.timings[phase].toFixed(3) + "s");
@@ -381,18 +370,6 @@ function parse_source_map() {
}
}
function to_cache(key) {
if (cache[key]) {
cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
} else {
cache[key] = {
cname: -1,
props: new UglifyJS.Dictionary()
};
}
return cache[key];
}
function skip_key(key) {
return skip_keys.indexOf(key) >= 0;
}

View File

@@ -92,12 +92,9 @@ function Compressor(options, false_by_default) {
var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) {
if (/^@/.test(key) && HOP(global_defs, key)) {
var ast = parse(global_defs[key]);
if (ast.body.length == 1 && ast.body[0] instanceof AST_SimpleStatement) {
global_defs[key.slice(1)] = ast.body[0].body;
} else throw new Error(string_template("Can't handle expression: {value}", {
value: global_defs[key]
}));
global_defs[key.slice(1)] = parse(global_defs[key], {
expression: true
});
}
}
var pure_funcs = this.options["pure_funcs"];
@@ -124,13 +121,13 @@ function Compressor(options, false_by_default) {
};
}
var toplevel = this.options["toplevel"];
if (typeof toplevel == "string") {
this.toplevel.funcs = /funcs/.test(toplevel);
this.toplevel.vars = /vars/.test(toplevel);
} else {
this.toplevel = toplevel ? return_true : return_false;
this.toplevel.funcs = this.toplevel.vars = toplevel;
}
this.toplevel = typeof toplevel == "string" ? {
funcs: /funcs/.test(toplevel),
vars: /vars/.test(toplevel)
} : {
funcs: toplevel,
vars: toplevel
};
var sequences = this.options["sequences"];
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
this.warnings_produced = {};
@@ -139,11 +136,11 @@ function Compressor(options, false_by_default) {
Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
toplevel: function(def) {
for (var i = 0, len = def.orig.length; i < len; i++)
exposed: function(def) {
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return false;
return true;
return true;
return false;
},
compress: function(node) {
if (this.option("expression")) {
@@ -279,11 +276,11 @@ merge(Compressor.prototype, {
var reduce_vars = rescan && compressor.option("reduce_vars");
var safe_ids = Object.create(null);
var suppressor = new TreeWalker(function(node) {
if (node instanceof AST_Symbol) {
var d = node.definition();
if (node instanceof AST_SymbolRef) d.references.push(node);
d.fixed = false;
}
if (!(node instanceof AST_Symbol)) return;
var d = node.definition();
if (!d) return;
if (node instanceof AST_SymbolRef) d.references.push(node);
d.fixed = false;
});
var tw = new TreeWalker(function(node, descend) {
node._squeezed = false;
@@ -345,7 +342,7 @@ merge(Compressor.prototype, {
}
if (node instanceof AST_Defun) {
var d = node.name.definition();
if (d.global && !compressor.toplevel(d) || safe_to_read(d)) {
if (compressor.exposed(d) || safe_to_read(d)) {
d.fixed = false;
} else {
d.fixed = node;
@@ -517,7 +514,7 @@ merge(Compressor.prototype, {
def.escaped = false;
if (def.scope.uses_eval) {
def.fixed = false;
} else if (!def.global || compressor.toplevel(def)) {
} else if (!compressor.exposed(def)) {
def.fixed = undefined;
} else {
def.fixed = false;
@@ -547,8 +544,25 @@ merge(Compressor.prototype, {
return fixed();
});
AST_SymbolRef.DEFMETHOD("is_immutable", function() {
var orig = this.definition().orig;
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda;
});
function is_lhs_read_only(lhs) {
return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda;
if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression;
if (lhs instanceof AST_SymbolRef) {
if (lhs.is_immutable()) return false;
lhs = lhs.fixed_value();
}
if (!lhs) return true;
if (lhs instanceof AST_RegExp) return false;
if (lhs instanceof AST_Constant) return true;
return is_lhs_read_only(lhs);
}
return false;
}
function find_variable(compressor, name) {
@@ -700,15 +714,23 @@ merge(Compressor.prototype, {
var candidates = [];
var stat_index = statements.length;
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
// would be translated into equivalent assignments:
// var a = x(), b = undefined;
if (stat_index == 0 && compressor.option("unused")) extract_args();
// Find collapsible assignments
extract_candidates(statements[stat_index]);
while (candidates.length > 0) {
var candidate = candidates.pop();
var lhs = get_lhs(candidate);
if (!lhs || is_lhs_read_only(lhs)) continue;
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var side_effects = value_has_side_effects(candidate);
var hit = false, abort = false, replaced = false;
var hit = candidate.name instanceof AST_SymbolFunarg;
var abort = false, replaced = false;
var tt = new TreeTransformer(function(node, descend) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
@@ -748,7 +770,7 @@ merge(Compressor.prototype, {
}
if (candidate instanceof AST_VarDef) {
var def = candidate.name.definition();
if (def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
if (def.references.length == 1 && !compressor.exposed(def)) {
return maintain_this_binding(parent, node, candidate.value);
}
return make_node(AST_Assign, candidate, {
@@ -789,6 +811,35 @@ merge(Compressor.prototype, {
}
}
function extract_args() {
var iife, fn = compressor.self();
if (fn instanceof AST_Function
&& !fn.name
&& !fn.uses_arguments
&& !fn.uses_eval
&& (iife = compressor.parent()) instanceof AST_Call
&& iife.expression === fn) {
fn.argnames.forEach(function(sym, i) {
var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym);
else arg.walk(new TreeWalker(function(node) {
if (!arg) return true;
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
var s = node.definition().scope;
if (s !== scope) while (s = s.parent_scope) {
if (s === scope) return true;
}
arg = null;
}
}));
if (arg) candidates.push(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
});
}
}
function extract_candidates(expr) {
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
|| expr instanceof AST_Unary && (expr.operator == "++" || expr.operator == "--")) {
@@ -809,8 +860,8 @@ merge(Compressor.prototype, {
function get_lhs(expr) {
if (expr instanceof AST_VarDef) {
var def = expr.name.definition();
if (def.orig.length > 1
|| def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg)
|| def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name);
}
} else {
@@ -851,6 +902,14 @@ merge(Compressor.prototype, {
}
function remove_candidate(expr) {
if (expr.name instanceof AST_SymbolFunarg) {
var index = compressor.self().argnames.indexOf(expr.name);
var args = compressor.parent().args;
if (args[index]) args[index] = make_node(AST_Number, args[index], {
value: 0
});
return true;
}
var found = false;
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
if (found) return node;
@@ -1287,6 +1346,7 @@ merge(Compressor.prototype, {
def(AST_SymbolRef, function(pure_getters) {
if (this.is_undefined) return true;
if (!is_strict(pure_getters)) return false;
if (this.is_immutable()) return false;
var fixed = this.fixed_value();
return !fixed || fixed._throw_on_access(pure_getters);
});
@@ -3105,7 +3165,7 @@ merge(Compressor.prototype, {
// 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) {
var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
return arg.value;
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
var ast = parse(code);
@@ -3114,7 +3174,9 @@ merge(Compressor.prototype, {
var comp = new Compressor(compressor.options);
ast = ast.transform(comp);
ast.figure_out_scope(mangle);
ast.mangle_names();
base54.reset();
ast.compute_char_frequency(mangle);
ast.mangle_names(mangle);
var fun;
ast.walk(new TreeWalker(function(node) {
if (fun) return true;
@@ -3123,18 +3185,18 @@ merge(Compressor.prototype, {
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;
self.args = [
make_node(AST_String, self, {
value: fun.argnames.map(function(arg) {
return arg.print_to_string();
}).join(",")
}),
make_node(AST_String, self.args[self.args.length - 1], {
value: code.get().replace(/^\{|\}$/g, "")
})
];
return self;
} catch (ex) {
if (ex instanceof JS_Parse_Error) {
@@ -3151,80 +3213,53 @@ merge(Compressor.prototype, {
var value = stat.value;
if (!value || value.is_constant_expression()) {
var args = self.args.concat(value || make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
return make_sequence(self, args).optimize(compressor);
}
}
if (exp instanceof AST_Function) {
if (compressor.option("inline")
&& !exp.name
&& exp.body.length == 1
&& !exp.uses_arguments
&& !exp.uses_eval
&& exp.body.length == 1
&& all(exp.argnames, function(arg) {
return arg.__unused;
})
&& !self.has_pure_annotation(compressor)) {
var value;
if (stat instanceof AST_Return) {
value = stat.value.clone(true);
value = stat.value;
} else if (stat instanceof AST_SimpleStatement) {
value = make_node(AST_UnaryPrefix, stat, {
operator: "void",
expression: stat.body.clone(true)
expression: stat.body
});
}
if (value) {
var fn = exp.clone();
fn.argnames = [];
fn.body = [];
if (exp.argnames.length > 0) {
fn.body.push(make_node(AST_Var, self, {
definitions: exp.argnames.map(function(sym, i) {
var arg = self.args[i];
return make_node(AST_VarDef, sym, {
name: sym,
value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
});
})
}));
}
if (self.args.length > exp.argnames.length) {
fn.body.push(make_node(AST_SimpleStatement, self, {
body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
return node.clone(true);
}))
}));
}
fn.body.push(make_node(AST_Return, self, {
value: value
}));
var body = fn.transform(compressor).body;
if (body.length == 0) return make_node(AST_Undefined, self);
if (body.length == 1 && body[0] instanceof AST_Return) {
value = body[0].value;
if (!value) return make_node(AST_Undefined, self);
var tw = new TreeWalker(function(node) {
if (value === self) return true;
if (node instanceof AST_SymbolRef) {
var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = self;
return true;
}
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = self;
var tw = new TreeWalker(function(node) {
if (!value) return true;
if (node instanceof AST_SymbolRef) {
var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = null;
return true;
}
});
value.walk(tw);
if (value !== self) value = best_of(compressor, value, self);
} else {
value = self;
}
if (value !== self) return value;
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = null;
return true;
}
});
value.walk(tw);
}
if (value) {
var args = self.args.concat(value);
return make_sequence(self, args).optimize(compressor);
}
}
if (compressor.option("side_effects") && all(exp.body, is_empty)) {
var args = self.args.concat(make_node(AST_Undefined, self));
return make_sequence(self, args).transform(compressor);
return make_sequence(self, args).optimize(compressor);
}
}
if (compressor.option("drop_console")) {
@@ -3357,6 +3392,8 @@ merge(Compressor.prototype, {
|| cdr instanceof AST_PropAccess
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
field = "expression";
} else if (cdr instanceof AST_Conditional) {
field = "condition";
} else {
expressions[++i] = expressions[j];
break;
@@ -3887,7 +3924,7 @@ merge(Compressor.prototype, {
var d = self.definition();
var fixed = self.fixed_value();
if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true);
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
}
if (compressor.option("unused")
&& fixed instanceof AST_Function
@@ -3895,7 +3932,7 @@ merge(Compressor.prototype, {
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)
&& !d.scope.uses_eval
&& compressor.find_parent(AST_Scope) === fixed.parent_scope) {
return fixed;
return fixed.clone(true);
}
if (compressor.option("evaluate") && fixed) {
if (d.should_replace === undefined) {
@@ -3918,7 +3955,7 @@ merge(Compressor.prototype, {
}
var name_length = d.name.length;
var overhead = 0;
if (compressor.option("unused") && (!d.global || compressor.toplevel(d))) {
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / d.references.length;
}
d.should_replace = value_length <= name_length + overhead ? fn : false;

View File

@@ -27,6 +27,23 @@ function set_shorthand(name, options, keys) {
}
}
function init_cache(cache) {
if (!cache) return;
if (!("cname" in cache)) cache.cname = -1;
if (!("props" in cache)) {
cache.props = new Dictionary();
} else if (!(cache.props instanceof Dictionary)) {
cache.props = Dictionary.fromObject(cache.props);
}
}
function to_json(cache) {
return {
cname: cache.cname,
props: cache.props.toObject()
};
}
function minify(files, options) {
var warn_function = AST_Node.warn_function;
try {
@@ -35,6 +52,7 @@ function minify(files, options) {
ie8: false,
keep_fnames: false,
mangle: {},
nameCache: null,
output: {},
parse: {},
sourceMap: false,
@@ -52,7 +70,7 @@ function minify(files, options) {
set_shorthand("warnings", options, [ "compress" ]);
if (options.mangle) {
options.mangle = defaults(options.mangle, {
cache: null,
cache: options.nameCache && (options.nameCache.vars || {}),
eval: false,
ie8: false,
keep_fnames: false,
@@ -60,6 +78,16 @@ function minify(files, options) {
reserved: [],
toplevel: false,
}, true);
if (options.nameCache && options.mangle.properties) {
if (typeof options.mangle.properties != "object") {
options.mangle.properties = {};
}
if (!("cache" in options.mangle.properties)) {
options.mangle.properties.cache = options.nameCache.props || {};
}
}
init_cache(options.mangle.cache);
init_cache(options.mangle.properties.cache);
}
if (options.sourceMap) {
options.sourceMap = defaults(options.sourceMap, {
@@ -153,6 +181,12 @@ function minify(files, options) {
}
}
}
if (options.nameCache && options.mangle) {
if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache);
if (options.mangle.properties && options.mangle.properties.cache) {
options.nameCache.props = to_json(options.mangle.properties.cache);
}
}
if (timings) {
timings.end = Date.now();
result.timings = {

View File

@@ -501,6 +501,7 @@ function OutputStream(options) {
use_asm = prev_use_asm;
}
});
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);

View File

@@ -1010,8 +1010,12 @@ function parse($TEXT, options) {
? (next(), var_(true))
: expression(true, true);
if (is("operator", "in")) {
if (init instanceof AST_Var && init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop");
if (init instanceof AST_Var) {
if (init.definitions.length > 1)
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
} else if (!is_assignable(init)) {
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
}
next();
return for_in(init);
}

View File

@@ -79,7 +79,7 @@ SymbolDef.prototype = {
if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope;
var def;
if (this.defun && (def = this.defun.variables.get(this.name))) {
if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name;
} else
this.mangled_name = s.next_mangled(options, this);
@@ -87,6 +87,9 @@ SymbolDef.prototype = {
cache.set(this.name, this.mangled_name);
}
}
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
}
};
@@ -206,6 +209,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.reference(options);
return true;
}
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
});
self.walk(tw);
@@ -348,7 +361,8 @@ AST_Function.DEFMETHOD("next_mangled", function(options, def){
});
AST_Symbol.DEFMETHOD("unmangleable", function(options){
return this.definition().unmangleable(options);
var def = this.definition();
return !def || def.unmangleable(options);
});
// labels are always mangleable
@@ -447,103 +461,69 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());
else if (node instanceof AST_Return)
base54.consider("return");
else if (node instanceof AST_Throw)
base54.consider("throw");
else if (node instanceof AST_Continue)
base54.consider("continue");
else if (node instanceof AST_Break)
base54.consider("break");
else if (node instanceof AST_Debugger)
base54.consider("debugger");
else if (node instanceof AST_Directive)
base54.consider(node.value);
else if (node instanceof AST_While)
base54.consider("while");
else if (node instanceof AST_Do)
base54.consider("do while");
else if (node instanceof AST_If) {
base54.consider("if");
if (node.alternative) base54.consider("else");
}
else if (node instanceof AST_Var)
base54.consider("var");
else if (node instanceof AST_Lambda)
base54.consider("function");
else if (node instanceof AST_For)
base54.consider("for");
else if (node instanceof AST_ForIn)
base54.consider("for in");
else if (node instanceof AST_Switch)
base54.consider("switch");
else if (node instanceof AST_Case)
base54.consider("case");
else if (node instanceof AST_Default)
base54.consider("default");
else if (node instanceof AST_With)
base54.consider("with");
else if (node instanceof AST_ObjectSetter)
base54.consider("set" + node.key);
else if (node instanceof AST_ObjectGetter)
base54.consider("get" + node.key);
else if (node instanceof AST_ObjectKeyVal)
base54.consider(node.key);
else if (node instanceof AST_New)
base54.consider("new");
else if (node instanceof AST_This)
base54.consider("this");
else if (node instanceof AST_Try)
base54.consider("try");
else if (node instanceof AST_Catch)
base54.consider("catch");
else if (node instanceof AST_Finally)
base54.consider("finally");
else if (node instanceof AST_Symbol && node.unmangleable(options))
base54.consider(node.name);
else if (node instanceof AST_Unary || node instanceof AST_Binary)
base54.consider(node.operator);
else if (node instanceof AST_Dot)
base54.consider(node.property);
});
this.walk(tw);
try {
AST_Node.prototype.print = function(stream, force_parens) {
this._print(stream, force_parens);
if (this instanceof AST_Symbol && !this.unmangleable(options)) {
base54.consider(this.name, -1);
} else if (options.properties) {
if (this instanceof AST_Dot) {
base54.consider(this.property, -1);
} else if (this instanceof AST_Sub) {
skip_string(this.property);
}
}
};
base54.consider(this.print_to_string(), 1);
} finally {
AST_Node.prototype.print = AST_Node.prototype._print;
}
base54.sort();
function skip_string(node) {
if (node instanceof AST_String) {
base54.consider(node.value, -1);
} else if (node instanceof AST_Conditional) {
skip_string(node.consequent);
skip_string(node.alternative);
} else if (node instanceof AST_Sequence) {
skip_string(node.expressions[node.expressions.length - 1]);
}
}
});
var base54 = (function() {
var string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
var leading = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_".split("");
var digits = "0123456789".split("");
var chars, frequency;
function reset() {
frequency = Object.create(null);
chars = string.split("").map(function(ch){ return ch.charCodeAt(0) });
chars.forEach(function(ch){ frequency[ch] = 0 });
leading.forEach(function(ch) {
frequency[ch] = 0;
});
digits.forEach(function(ch) {
frequency[ch] = 0;
});
}
base54.consider = function(str){
base54.consider = function(str, delta) {
for (var i = str.length; --i >= 0;) {
var code = str.charCodeAt(i);
if (code in frequency) ++frequency[code];
frequency[str[i]] += delta;
}
};
function compare(a, b) {
return frequency[b] - frequency[a];
}
base54.sort = function() {
chars = mergeSort(chars, function(a, b){
if (is_digit(a) && !is_digit(b)) return 1;
if (is_digit(b) && !is_digit(a)) return -1;
return frequency[b] - frequency[a];
});
chars = mergeSort(leading, compare).concat(mergeSort(digits, compare));
};
base54.reset = reset;
reset();
base54.get = function(){ return chars };
base54.freq = function(){ return frequency };
function base54(num) {
var ret = "", base = 54;
num++;
do {
num--;
ret += String.fromCharCode(chars[num % base]);
ret += chars[num % base];
num = Math.floor(num / base);
base = 64;
} while (num > 0);

View File

@@ -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.0.18",
"version": "3.0.23",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -1978,10 +1978,10 @@ chained_3: {
}
expect: {
console.log(function(a, b) {
var c = a, c = b;
var c = 1, c = b;
b++;
return c;
}(1, 2));
}(0, 2));
}
expect_stdout: "2"
}
@@ -2186,3 +2186,73 @@ compound_assignment: {
}
expect_stdout: "4"
}
issue_2187_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
}(function() {
console.log(a);
});
}
expect: {
var a = 1;
!function(foo) {
foo();
var a = 2;
console.log(a);
}(function() {
console.log(a);
});
}
expect_stdout: [
"1",
"2",
]
}
issue_2187_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var b = 1;
console.log(function(a) {
return a && ++b;
}(b--));
}
expect: {
var b = 1;
console.log(function(a) {
return b-- && ++b;
}());
}
expect_stdout: "1"
}
issue_2187_3: {
options = {
collapse_vars: true,
inline: true,
unused: true,
}
input: {
var b = 1;
console.log(function(a) {
return a && ++b;
}(b--));
}
expect: {
var b = 1;
console.log(b-- && ++b);
}
expect_stdout: "1"
}

View File

@@ -1113,6 +1113,7 @@ issue_2105: {
options = {
collapse_vars: true,
inline: true,
passes: 3,
reduce_vars: true,
side_effects: true,
unused: true,
@@ -1138,7 +1139,7 @@ issue_2105: {
});
}
expect: {
!void function() {
(function() {
var quux = function() {
console.log("PASS");
};
@@ -1148,7 +1149,7 @@ issue_2105: {
quux();
}
};
}().prop();
})().prop();
}
expect_stdout: "PASS"
}

View File

@@ -265,7 +265,7 @@ issue_203: {
}
expect: {
var m = {};
var fn = Function("a", "b", "b.exports=42");
var fn = Function("n,o", "o.exports=42");
fn(null, m, m.exports);
console.log(m.exports);
}
@@ -468,11 +468,9 @@ issue_2114_1: {
}
expect: {
var c = 0;
!function() {
0;
}((c += 1, c = 1 + c, function() {
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}()));
}();
console.log(c);
}
expect_stdout: "2"

View File

@@ -174,3 +174,24 @@ issue_1986: {
console.log(42);
}
}
issue_2167: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
global_defs: {
"@isDevMode": "function(){}",
},
side_effects: true,
}
input: {
if (isDevMode()) {
greetOverlord();
}
doWork();
}
expect: {
doWork();
}
}

View File

@@ -10,9 +10,9 @@ issue_1321_no_debug: {
}
expect: {
var x = {};
x.b = 1;
x["a"] = 2 * x.b;
console.log(x.b, x["a"]);
x.o = 1;
x["a"] = 2 * x.o;
console.log(x.o, x["a"]);
}
expect_stdout: true
}
@@ -30,9 +30,9 @@ issue_1321_debug: {
}
expect: {
var x = {};
x.a = 1;
x["_$foo$_"] = 2 * x.a;
console.log(x.a, x["_$foo$_"]);
x.o = 1;
x["_$foo$_"] = 2 * x.o;
console.log(x.o, x["_$foo$_"]);
}
expect_stdout: true
}
@@ -49,9 +49,9 @@ issue_1321_with_quoted: {
}
expect: {
var x = {};
x.a = 1;
x["b"] = 2 * x.a;
console.log(x.a, x["b"]);
x.o = 1;
x["x"] = 2 * x.o;
console.log(x.o, x["x"]);
}
expect_stdout: true
}

View File

@@ -82,7 +82,7 @@ numeric_literal: {
' 42: 2,',
' "42": 3,',
' 37: 4,',
' a: 5,',
' o: 5,',
' 1e42: 6,',
' b: 7,',
' "1e+42": 8',
@@ -92,7 +92,7 @@ numeric_literal: {
'',
'console.log(obj[42], obj["42"]);',
'',
'console.log(obj[37], obj["a"], obj[37], obj["37"]);',
'console.log(obj[37], obj["o"], obj[37], obj["37"]);',
'',
'console.log(obj[1e42], obj["b"], obj["1e+42"]);',
]
@@ -173,32 +173,32 @@ identifier: {
}
expect: {
var obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5,
f: 6,
g: 7,
h: 8,
i: 9,
j: 10,
k: 11,
l: 12,
m: 13,
n: 14,
o: 15,
p: 16,
q: 17,
r: 18,
s: 19,
t: 20,
u: 21,
v: 22,
w: 23,
x: 24,
y: 25,
z: 26,
e: 1,
t: 2,
n: 3,
a: 4,
i: 5,
o: 6,
r: 7,
l: 8,
s: 9,
c: 10,
f: 11,
u: 12,
d: 13,
h: 14,
p: 15,
b: 16,
v: 17,
w: 18,
y: 19,
g: 20,
m: 21,
k: 22,
x: 23,
j: 24,
z: 25,
q: 26,
A: 27,
B: 28,
C: 29,
@@ -229,11 +229,11 @@ identifier: {
Z: 54,
$: 55,
_: 56,
aa: 57,
ba: 58,
ca: 59,
da: 60,
ea: 61,
ee: 57,
te: 58,
ne: 59,
ae: 60,
ie: 61,
};
}
}

View File

@@ -419,7 +419,7 @@ wrap_iife_in_return_call: {
expect_exact: '(void console.log("test"))();'
}
pure_annotation: {
pure_annotation_1: {
options = {
inline: true,
side_effects: true,
@@ -432,6 +432,20 @@ pure_annotation: {
expect_exact: ""
}
pure_annotation_2: {
options = {
collapse_vars: true,
inline: true,
side_effects: true,
}
input: {
/*@__PURE__*/(function(n) {
console.log("hello", n);
}(42));
}
expect_exact: ""
}
drop_fargs: {
options = {
cascade: true,
@@ -449,9 +463,7 @@ drop_fargs: {
}
expect: {
var a = 1;
!function() {
a++;
}(++a && a.var);
++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"
@@ -474,9 +486,7 @@ keep_fargs: {
}
expect: {
var a = 1;
!function(a_1) {
a++;
}(++a && a.var);
++a && a.var, a++;
console.log(a);
}
expect_stdout: "3"

View File

@@ -1,37 +1,41 @@
dont_reuse_prop: {
mangle_props = {
regex: /asd/
};
}
input: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.a = 123;
obj.asd = 256;
console.log(obj.a);
}
expect: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.a = 123;
obj.b = 256;
console.log(obj.a);
}
expect_stdout: "123"
}
unmangleable_props_should_always_be_reserved: {
mangle_props = {
regex: /asd/
};
}
input: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.asd = 256;
obj.a = 123;
console.log(obj.a);
}
expect: {
"aaaaaaaaaabbbbb";
var obj = {};
obj.b = 256;
obj.a = 123;
console.log(obj.a);
}
}
expect_stdout: "123"
}

View File

@@ -135,11 +135,11 @@ mangle_properties: {
a['run']({color: "blue", foo: "baz"});
}
expect: {
a["a"] = "bar";
a.b = "red";
x = {c: 10};
a.d(x.c, a.a);
a['d']({b: "blue", a: "baz"});
a["o"] = "bar";
a.a = "red";
x = {r: 10};
a.b(x.r, a.o);
a['b']({a: "blue", o: "baz"});
}
}
@@ -177,16 +177,16 @@ mangle_unquoted_properties: {
function f1() {
a["foo"] = "bar";
a.color = "red";
a.b = 2;
x = {"bar": 10, c: 7};
a.c = 9;
a.o = 2;
x = {"bar": 10, f: 7};
a.f = 9;
}
function f2() {
a.foo = "bar";
a['color'] = "red";
x = {bar: 10, c: 7};
a.c = 9;
a.b = 3;
x = {bar: 10, f: 7};
a.f = 9;
a.o = 3;
}
}
}

View File

@@ -241,3 +241,147 @@ issue_2110_2: {
}
expect_stdout: "function"
}
set_immutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: "PASS"
}
set_immutable_3: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
1..foo += "";
if (1..foo) console.log("FAIL");
else console.log("PASS");
}
expect_stdout: true
}
set_immutable_4: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
}
input: {
"use strict";
var a = 1;
a.foo += "";
if (a.foo) console.log("FAIL");
else console.log("PASS");
}
expect: {
"use strict";
var a = 1;
a.foo += "", a.foo ? console.log("FAIL") : console.log("PASS");
}
expect_stdout: true
}
set_mutable_1: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
if (a.foo += "") console.log("PASS");
else console.log("FAIL");
}();
}
expect_stdout: "PASS"
}
set_mutable_2: {
options = {
cascade: true,
conditionals: true,
pure_getters: "strict",
reduce_vars: true,
sequences: true,
side_effects: true,
}
input: {
!function a() {
a.foo += "";
if (a.foo) console.log("PASS");
else console.log("FAIL");
}();
}
expect: {
!function a() {
(a.foo += "") ? console.log("PASS") : console.log("FAIL");
}();
}
expect_stdout: "PASS"
}

View File

@@ -2575,3 +2575,28 @@ accessor: {
}
expect_stdout: "1 1"
}
for_in_prop: {
options = {
reduce_vars: true,
}
input: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect: {
var a = {
foo: function() {
for (this.b in [1, 2]);
}
};
a.foo();
console.log(a.b);
}
expect_stdout: "1"
}

View File

@@ -255,3 +255,73 @@ issue_1586_2: {
}
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
}
issue_2120_1: {
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}

View File

@@ -0,0 +1,4 @@
var a, b = [1, 2];
for (1, 2, a in b) {
console.log(a, b[a]);
}

View File

@@ -0,0 +1,4 @@
var c = [1, 2];
for (var a, b in c) {
console.log(a, c[a]);
}

View File

@@ -518,6 +518,36 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should throw syntax error (for-in init)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_1.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-in_1.js:2,5",
"for (1, 2, a in b) {",
" ^",
"ERROR: Invalid left-hand side in for..in loop"
].join("\n"));
done();
});
});
it("Should throw syntax error (for-in var)", function(done) {
var command = uglifyjscmd + ' test/input/invalid/for-in_2.js';
exec(command, function (err, stdout, stderr) {
assert.ok(err);
assert.strictEqual(stdout, "");
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
"Parse error at test/input/invalid/for-in_2.js:2,5",
"for (var a, b in c) {",
" ^",
"ERROR: Only one variable declaration allowed in for..in loop"
].join("\n"));
done();
});
});
it("Should handle literal string as source map input", function(done) {
var command = [
uglifyjscmd,

View File

@@ -26,12 +26,12 @@ describe("bin/uglifyjs with input file globs", function() {
});
});
it("bin/uglifyjs with multiple input file globs.", function(done) {
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel';
var command = uglifyjscmd + ' "test/input/issue-1242/???.es5" "test/input/issue-1242/*.js" -mc toplevel,passes=2';
exec(command, function(err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",2*11);\n');
assert.strictEqual(stdout, 'var print=console.log.bind(console);print("qux",9,6),print("Foo:",22);\n');
done();
});
});

View File

@@ -2,29 +2,37 @@ var Uglify = require('../../');
var assert = require("assert");
describe("let", function() {
it("Should not produce `let` as a variable name in mangle", function(done) {
it("Should not produce reserved keywords as variable name in mangle", function(done) {
this.timeout(10000);
// Produce a lot of variables in a function and run it through mangle.
var s = '"use strict"; function foo() {';
for (var i = 0; i < 21000; ++i) {
var s = '"dddddeeeeelllllooooottttt"; function foo() {';
for (var i = 0; i < 18000; i++) {
s += "var v" + i + "=0;";
}
s += '}';
var result = Uglify.minify(s, {compress: false});
// Verify that select keywords and reserved keywords not produced
assert.strictEqual(result.code.indexOf("var let="), -1);
assert.strictEqual(result.code.indexOf("var do="), -1);
assert.strictEqual(result.code.indexOf("var var="), -1);
[
"do",
"let",
"var",
].forEach(function(name) {
assert.strictEqual(result.code.indexOf("var " + name + "="), -1);
});
// Verify that the variable names that appeared immediately before
// and after the erroneously generated `let` variable name still exist
// and after the erroneously generated variable name still exist
// to show the test generated enough symbols.
assert(result.code.indexOf("var ket=") >= 0);
assert(result.code.indexOf("var met=") >= 0);
[
"to", "eo",
"eet", "fet",
"rar", "oar",
].forEach(function(name) {
assert.ok(result.code.indexOf("var " + name + "=") >= 0);
});
done();
});
});

View File

@@ -1,6 +1,7 @@
var Uglify = require('../../');
var assert = require("assert");
var readFileSync = require("fs").readFileSync;
var run_code = require("../sandbox").run_code;
function read(path) {
return readFileSync(path, "utf8");
@@ -20,6 +21,58 @@ describe("minify", function() {
assert.strictEqual(result.code, "alert(2);");
});
it("Should work with mangle.cache", function() {
var cache = {};
var original = "";
var compressed = "";
[
"bar.es5",
"baz.es5",
"foo.es5",
"qux.js",
].forEach(function(file) {
var code = read("test/input/issue-1242/" + file);
var result = Uglify.minify(code, {
mangle: {
cache: cache,
toplevel: true
}
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 20), '{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
assert.strictEqual(run_code(compressed), run_code(original));
});
it("Should work with nameCache", function() {
var cache = {};
var original = "";
var compressed = "";
[
"bar.es5",
"baz.es5",
"foo.es5",
"qux.js",
].forEach(function(file) {
var code = read("test/input/issue-1242/" + file);
var result = Uglify.minify(code, {
mangle: {
toplevel: true
},
nameCache: cache
});
if (result.error) throw result.error;
original += code;
compressed += result.code;
});
assert.strictEqual(JSON.stringify(cache).slice(0, 28), '{"vars":{"cname":5,"props":{');
assert.strictEqual(compressed, 'function n(n){return 3*n}function r(n){return n/2}function c(o){l("Foo:",2*o)}var l=console.log.bind(console);var f=n(3),i=r(12);l("qux",f,i),c(11);');
assert.strictEqual(run_code(compressed), run_code(original));
});
describe("keep_quoted_props", function() {
it("Should preserve quotes in object literals", function() {
var js = 'var foo = {"x": 1, y: 2, \'z\': 3};';
@@ -212,7 +265,7 @@ describe("minify", function() {
});
var err = result.error;
assert.ok(err instanceof Error);
assert.strictEqual(err.stack.split(/\n/)[0], "Error: Can't handle expression: debugger");
assert.strictEqual(err.stack.split(/\n/)[0], "SyntaxError: Unexpected token: keyword (debugger)");
});
it("should skip inherited properties", function() {
var foo = Object.create({ skip: this });

View File

@@ -86,7 +86,6 @@ function run_compress_tests() {
log_start_file(file);
function test_case(test) {
log_test(test.name);
U.base54.reset();
var output_options = test.beautify || {};
var expect;
if (test.expect) {
@@ -101,9 +100,6 @@ function run_compress_tests() {
quote_style: 3,
keep_quoted_props: true
});
if (test.mangle_props) {
input = U.mangle_properties(input, test.mangle_props);
}
var options = U.defaults(test.options, {
warnings: false
});
@@ -118,10 +114,16 @@ function run_compress_tests() {
var cmp = new U.Compressor(options, true);
var output = cmp.compress(input);
output.figure_out_scope(test.mangle);
if (test.mangle) {
if (test.mangle || test.mangle_props) {
U.base54.reset();
output.compute_char_frequency(test.mangle);
}
if (test.mangle) {
output.mangle_names(test.mangle);
}
if (test.mangle_props) {
output = U.mangle_properties(output, test.mangle_props);
}
output = make_code(output, output_options);
if (expect != output) {
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {