diff --git a/README.md b/README.md index 3b99441b..b6abc379 100644 --- a/README.md +++ b/README.md @@ -350,6 +350,11 @@ to set `true`; it's effectively a shortcut for `foo=true`). compressor from discarding unused function arguments. You need this for code which relies on `Function.length`. +- `keep_fnames` -- default `false`. Pass `true` to prevent the + compressor from mangling/discarding function names. Useful for code relying on + `Function.prototype.name`. + + ### The `unsafe` option It enables some transformations that *might* break code logic in certain diff --git a/lib/compress.js b/lib/compress.js index 646476df..cbd1caee 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -2308,7 +2308,15 @@ merge(Compressor.prototype, { }); OPT(AST_SymbolRef, function(self, compressor){ - if (self.undeclared()) { + function isLHS(symbol, parent) { + return ( + parent instanceof AST_Binary && + parent.operator === '=' && + parent.left === symbol + ); + } + + if (self.undeclared() && !isLHS(self, compressor.parent())) { var defines = compressor.option("global_defs"); if (defines && defines.hasOwnProperty(self.name)) { return make_node_from_constant(compressor, defines[self.name], self); diff --git a/lib/propmangle.js b/lib/propmangle.js index 086709e6..840bda91 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -83,6 +83,7 @@ function mangle_properties(ast, options) { var regex = options.regex; var names_to_mangle = []; + var unmangleable = []; // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node){ @@ -108,20 +109,14 @@ function mangle_properties(ast, options) { // step 2: transform the tree, renaming properties return ast.transform(new TreeTransformer(function(node){ if (node instanceof AST_ObjectKeyVal) { - if (should_mangle(node.key)) { - node.key = mangle(node.key); - } + node.key = mangle(node.key); } else if (node instanceof AST_ObjectProperty) { // setter or getter - if (should_mangle(node.key.name)) { - node.key.name = mangle(node.key.name); - } + node.key.name = mangle(node.key.name); } else if (node instanceof AST_Dot) { - if (should_mangle(node.property)) { - node.property = mangle(node.property); - } + node.property = mangle(node.property); } else if (node instanceof AST_Sub) { node.property = mangleStrings(node.property); @@ -143,6 +138,7 @@ function mangle_properties(ast, options) { // only function declarations after this line function can_mangle(name) { + if (unmangleable.indexOf(name) >= 0) return false; if (reserved.indexOf(name) >= 0) return false; if (options.only_cache) { return cache.props.has(name); @@ -161,9 +157,17 @@ function mangle_properties(ast, options) { function add(name) { if (can_mangle(name)) push_uniq(names_to_mangle, name); + + if (!should_mangle(name)) { + push_uniq(unmangleable, name); + } } function mangle(name) { + if (!should_mangle(name)) { + return name; + } + var mangled = cache.props.get(name); if (!mangled) { do { @@ -206,9 +210,7 @@ function mangle_properties(ast, options) { node.cdr = mangleStrings(node.cdr); } else if (node instanceof AST_String) { - if (should_mangle(node.value)) { - node.value = mangle(node.value); - } + node.value = mangle(node.value); } else if (node instanceof AST_Conditional) { node.consequent = mangleStrings(node.consequent); diff --git a/test/compress/issue-208.js b/test/compress/issue-208.js new file mode 100644 index 00000000..14752b8a --- /dev/null +++ b/test/compress/issue-208.js @@ -0,0 +1,11 @@ +do_not_update_lhs: { + options = { global_defs: { DEBUG: false } }; + input: { DEBUG = false; } + expect: { DEBUG = false; } +} + +do_update_rhs: { + options = { global_defs: { DEBUG: false } }; + input: { MY_DEBUG = DEBUG; } + expect: { MY_DEBUG = false; } +} diff --git a/test/compress/issue-747.js b/test/compress/issue-747.js new file mode 100644 index 00000000..0a4e4502 --- /dev/null +++ b/test/compress/issue-747.js @@ -0,0 +1,37 @@ +dont_reuse_prop: { + mangle_props = { + regex: /asd/ + }; + + input: { + var obj = {}; + obj.a = 123; + obj.asd = 256; + console.log(obj.a); + } + expect: { + var obj = {}; + obj.a = 123; + obj.b = 256; + console.log(obj.a); + } +} + +unmangleable_props_should_always_be_reserved: { + mangle_props = { + regex: /asd/ + }; + + input: { + var obj = {}; + obj.asd = 256; + obj.a = 123; + console.log(obj.a); + } + expect: { + var obj = {}; + obj.b = 256; + obj.a = 123; + console.log(obj.a); + } +} \ No newline at end of file diff --git a/test/run-tests.js b/test/run-tests.js index 92b153ee..ea8d03e4 100755 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -96,6 +96,9 @@ function run_compress_tests() { } var input = as_toplevel(test.input); var input_code = make_code(test.input); + if (test.mangle_props) { + input = U.mangle_properties(input, test.mangle_props); + } var output = input.transform(cmp); output.figure_out_scope(); output = make_code(output, false); diff --git a/tools/node.js b/tools/node.js index cbe49e39..29d5632d 100644 --- a/tools/node.js +++ b/tools/node.js @@ -73,17 +73,20 @@ exports.minify = function(files, options) { } else { if (typeof files == "string") files = [ files ]; - files.forEach(function(file){ + files.forEach(function(file, i){ var code = options.fromString ? file : fs.readFileSync(file, "utf8"); sourcesContent[file] = code; toplevel = UglifyJS.parse(code, { - filename: options.fromString ? "?" : file, + filename: options.fromString ? i : file, toplevel: toplevel }); }); } + if (options.wrap) { + toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll); + } // 2. compress if (options.compress) {