aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md16
-rwxr-xr-xbin/uglifyjs4
-rw-r--r--lib/propmangle.js34
-rw-r--r--test/compress/issue-1321.js54
-rw-r--r--test/compress/properties.js92
5 files changed, 195 insertions, 5 deletions
diff --git a/README.md b/README.md
index 789219dc..6e9e5f2f 100644
--- a/README.md
+++ b/README.md
@@ -285,6 +285,21 @@ of mangled property names.
Using the name cache is not necessary if you compress all your files in a
single call to UglifyJS.
+#### Debugging property name mangling
+
+You can also pass `--mangle-props-debug` in order to mangle property names
+without completely obscuring them. For example the property `o.foo`
+would mangle to `o._$foo$_` with this option. This allows property mangling
+of a large codebase while still being able to debug the code and identify
+where mangling is breaking things.
+
+You can also pass a custom suffix using `--mangle-props-debug=XYZ`. This would then
+mangle `o.foo` to `o._$foo$XYZ_`. You can change this each time you compile a
+script to identify how a property got mangled. One technique is to pass a
+random number on every compile to simulate mangling changing with different
+inputs (e.g. as you update the input script with new properties), and to help
+identify mistakes like writing mangled keys to storage.
+
## Compressor options
You need to pass `--compress` (`-c`) to enable the compressor. Optionally
@@ -749,6 +764,7 @@ Other options:
- `regex` — Pass a RegExp to only mangle certain names (maps to the `--mangle-regex` CLI arguments option)
- `ignore_quoted` – Only mangle unquoted property names (maps to the `--mangle-props 2` CLI arguments option)
+ - `debug` – Mangle names with the original name still present (maps to the `--mangle-props-debug` CLI arguments option). Defaults to `false`. Pass an empty string to enable, or a non-empty string to set the suffix.
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
diff --git a/bin/uglifyjs b/bin/uglifyjs
index ce2e9411..d5025827 100755
--- a/bin/uglifyjs
+++ b/bin/uglifyjs
@@ -98,6 +98,7 @@ You need to pass an argument to this option to specify the name that your module
.string("beautify")
.string("m")
.string("mangle")
+ .string("mangle-props-debug")
.string("c")
.string("compress")
.string("d")
@@ -419,7 +420,8 @@ async.eachLimit(files, 1, function (file, cb) {
cache : cache,
only_cache : !ARGS.mangle_props,
regex : regex,
- ignore_quoted : ARGS.mangle_props == 2
+ ignore_quoted : ARGS.mangle_props == 2,
+ debug : typeof ARGS.mangle_props_debug === "undefined" ? false : ARGS.mangle_props_debug
});
writeNameCache("props", cache);
})();
diff --git a/lib/propmangle.js b/lib/propmangle.js
index 3923baa6..f2777475 100644
--- a/lib/propmangle.js
+++ b/lib/propmangle.js
@@ -66,7 +66,8 @@ function mangle_properties(ast, options) {
cache : null,
only_cache : false,
regex : null,
- ignore_quoted : false
+ ignore_quoted : false,
+ debug : false
});
var reserved = options.reserved;
@@ -84,6 +85,15 @@ function mangle_properties(ast, options) {
var regex = options.regex;
var ignore_quoted = options.ignore_quoted;
+ // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
+ // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
+ // the same as passing an empty string.
+ var debug = (options.debug !== false);
+ var debug_name_suffix;
+ if (debug) {
+ debug_name_suffix = (options.debug === true ? "" : options.debug);
+ }
+
var names_to_mangle = [];
var unmangleable = [];
var ignored = {};
@@ -177,9 +187,25 @@ function mangle_properties(ast, options) {
var mangled = cache.props.get(name);
if (!mangled) {
- do {
- mangled = base54(++cache.cname);
- } while (!can_mangle(mangled));
+ if (debug) {
+ // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
+ var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
+
+ if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
+ mangled = debug_mangled;
+ }
+ }
+
+ // either debug mode is off, or it is on and we could not use the mangled name
+ if (!mangled) {
+ // note can_mangle() does not check if the name collides with the 'ignored' set
+ // (filled with quoted properties when ignore_quoted set). Make sure we add this
+ // check so we don't collide with a quoted name.
+ do {
+ mangled = base54(++cache.cname);
+ } while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
+ }
+
cache.props.set(name, mangled);
}
return mangled;
diff --git a/test/compress/issue-1321.js b/test/compress/issue-1321.js
new file mode 100644
index 00000000..2b6f17bf
--- /dev/null
+++ b/test/compress/issue-1321.js
@@ -0,0 +1,54 @@
+issue_1321_no_debug: {
+ mangle_props = {
+ ignore_quoted: true
+ }
+ input: {
+ var x = {};
+ x.foo = 1;
+ x["a"] = 2 * x.foo;
+ console.log(x.foo, x["a"]);
+ }
+ expect: {
+ var x = {};
+ x.b = 1;
+ x["a"] = 2 * x.b;
+ console.log(x.b, x["a"]);
+ }
+}
+
+issue_1321_debug: {
+ mangle_props = {
+ ignore_quoted: true,
+ debug: ""
+ }
+ input: {
+ var x = {};
+ x.foo = 1;
+ x["_$foo$_"] = 2 * x.foo;
+ console.log(x.foo, x["_$foo$_"]);
+ }
+ expect: {
+ var x = {};
+ x.a = 1;
+ x["_$foo$_"] = 2 * x.a;
+ console.log(x.a, x["_$foo$_"]);
+ }
+}
+
+issue_1321_with_quoted: {
+ mangle_props = {
+ ignore_quoted: false
+ }
+ input: {
+ var x = {};
+ x.foo = 1;
+ x["a"] = 2 * x.foo;
+ console.log(x.foo, x["a"]);
+ }
+ expect: {
+ var x = {};
+ x.a = 1;
+ x["b"] = 2 * x.a;
+ console.log(x.a, x["b"]);
+ }
+}
diff --git a/test/compress/properties.js b/test/compress/properties.js
index f1680808..22f2c339 100644
--- a/test/compress/properties.js
+++ b/test/compress/properties.js
@@ -142,6 +142,98 @@ mangle_unquoted_properties: {
}
}
+mangle_debug: {
+ mangle_props = {
+ debug: ""
+ };
+ input: {
+ a.foo = "bar";
+ x = { baz: "ban" };
+ }
+ expect: {
+ a._$foo$_ = "bar";
+ x = { _$baz$_: "ban" };
+ }
+}
+
+mangle_debug_true: {
+ mangle_props = {
+ debug: true
+ };
+ input: {
+ a.foo = "bar";
+ x = { baz: "ban" };
+ }
+ expect: {
+ a._$foo$_ = "bar";
+ x = { _$baz$_: "ban" };
+ }
+}
+
+mangle_debug_suffix: {
+ mangle_props = {
+ debug: "XYZ"
+ };
+ input: {
+ a.foo = "bar";
+ x = { baz: "ban" };
+ }
+ expect: {
+ a._$foo$XYZ_ = "bar";
+ x = { _$baz$XYZ_: "ban" };
+ }
+}
+
+mangle_debug_suffix_ignore_quoted: {
+ options = {
+ properties: false
+ }
+ mangle_props = {
+ ignore_quoted: true,
+ debug: "XYZ",
+ reserved: []
+ }
+ beautify = {
+ beautify: false,
+ quote_style: 3,
+ keep_quoted_props: true,
+ }
+ input: {
+ a.top = 1;
+ function f1() {
+ a["foo"] = "bar";
+ a.color = "red";
+ a.stuff = 2;
+ x = {"bar": 10, size: 7};
+ a.size = 9;
+ }
+ function f2() {
+ a.foo = "bar";
+ a['color'] = "red";
+ x = {bar: 10, size: 7};
+ a.size = 9;
+ a.stuff = 3;
+ }
+ }
+ expect: {
+ a._$top$XYZ_ = 1;
+ function f1() {
+ a["foo"] = "bar";
+ a.color = "red";
+ a._$stuff$XYZ_ = 2;
+ x = {"bar": 10, _$size$XYZ_: 7};
+ a._$size$XYZ_ = 9;
+ }
+ function f2() {
+ a.foo = "bar";
+ a['color'] = "red";
+ x = {bar: 10, _$size$XYZ_: 7};
+ a._$size$XYZ_ = 9;
+ a._$stuff$XYZ_ = 3;
+ }
+ }
+}
+
first_256_chars_as_properties: {
beautify = {
ascii_only: true,