diff --git a/lib/propmangle.js b/lib/propmangle.js index b6222990..6bd3a2f5 100644 --- a/lib/propmangle.js +++ b/lib/propmangle.js @@ -105,6 +105,7 @@ function mangle_properties(ast, options) { var names_to_mangle = []; var unmangleable = []; var ignored = {}; + var function_reserved = Object.create(null); // step 1: find candidates to mangle ast.walk(new TreeWalker(function(node){ @@ -125,6 +126,15 @@ function mangle_properties(ast, options) { // step 2: transform the tree, renaming properties return ast.transform(new TreeTransformer(function(node){ + if (node instanceof AST_Defun) { + function_reserved = Object.create(function_reserved); + + var reserve_props = parse_reserve_comments(node.start.comments_before); + reserve_props.forEach(function(prop) { + function_reserved[prop] = true; + }); + } + if (node instanceof AST_ObjectKeyVal) { if (!(ignore_quoted && node.quote)) node.key = mangle(node.key); @@ -152,6 +162,10 @@ function mangle_properties(ast, options) { // ); // } // } + }, function(node) { + if (node instanceof AST_Defun) { + function_reserved = Object.getPrototypeOf(function_reserved); + } })); // only function declarations after this line @@ -170,6 +184,8 @@ function mangle_properties(ast, options) { if (ignore_quoted && name in ignored) return false; if (regex && !regex.test(name)) return false; if (reserved.indexOf(name) >= 0) return false; + if (name in function_reserved) return false; + return cache.props.has(name) || names_to_mangle.indexOf(name) >= 0; } @@ -261,4 +277,21 @@ function mangle_properties(ast, options) { })); } + function parse_reserve_comments(comments) { + var props = []; + + comments.forEach(function(comment) { + var text = comment.value; + var reserveRe = /^\s*uglify-reserve (.+)/gm, + match; + + while (match = reserveRe.exec(text)) { + match[1].split(/\s+/).forEach(function(prop) { + push_uniq(props, prop); + }); + } + }); + + return props; + } } diff --git a/test/compress/reserve-comments.js b/test/compress/reserve-comments.js new file mode 100644 index 00000000..2b982191 --- /dev/null +++ b/test/compress/reserve-comments.js @@ -0,0 +1,126 @@ +reserve_single_line: { + mangle_props = true + + input: { + // noise + // uglify-reserve keepme andme + // noise + function foo() { + window.notme = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, butnotme: 0 }; + } + // noise + // uglify-reserve keepme + //uglify-reserve andme + // noise + function bar() { + window.notme = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, butnotme: 0 }; + } + } + expect: { + function foo() { + window.a = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, b: 0 }; + } + function bar() { + window.a = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, b: 0 }; + } + } +} + +reserve_multi_line: { + mangle_props = true + + input: { + /* uglify-reserve keepme andme*/ + function foo() { + window.notme = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, butnotme: 0 }; + } + /* noise + uglify-reserve keepme + uglify-reserve andme + noise */ + function bar() { + window.notme = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, butnotme: 0 }; + } + } + expect: { + function foo() { + window.a = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, b: 0 }; + } + function bar() { + window.a = 0; + window.keepme = 1; + window["andme"] = 1; + return { andme: 1, "keepme": 1, b: 0 }; + } + } +} + +reserve_propagation: { + mangle_props = true + + input: { + // uglify-reserve thisguy + function foo() { + // uglify-reserve thatguy + function bar() { + return { + notme: 0, + thisguy: 1, + thatguy: 1 + }; + } + return { + whome: 0, + thisguy: 1, + thatguy: 0 + }; + } + var o = { + whome: 0, + thisguy: 0, + thatguy: 0 + }; + } + expect: { + function foo() { + function bar() { + return { + a: 0, + thisguy: 1, + thatguy: 1 + }; + } + return { + b: 0, + thisguy: 1, + c: 0 + }; + } + var o = { + b: 0, + d: 0, + c: 0 + }; + } +}