1 /***********************************************************************
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS2
6 -------------------------------- (C) ---------------------------------
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
12 Distributed under the BSD license:
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 ***********************************************************************/
46 function find_builtins() {
48 [ Object, Array, Function, Number,
49 String, Boolean, Error, Math,
51 ].forEach(function(ctor){
52 Object.getOwnPropertyNames(ctor).map(add);
54 Object.getOwnPropertyNames(ctor.prototype).map(add);
63 function mangle_properties(ast, options) {
64 options = defaults(options, {
69 ignore_quoted : false,
73 var reserved = options.reserved;
75 reserved = find_builtins();
77 var cache = options.cache;
81 props: new Dictionary()
85 var regex = options.regex;
86 var ignore_quoted = options.ignore_quoted;
88 // note debug is either false (disabled), or a string of the debug suffix to use (enabled).
89 // note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
90 // the same as passing an empty string.
91 var debug = (options.debug !== false);
92 var debug_name_suffix;
94 debug_name_suffix = (options.debug === true ? "" : options.debug);
97 var names_to_mangle = [];
98 var unmangleable = [];
101 // step 1: find candidates to mangle
102 ast.walk(new TreeWalker(function(node){
103 if (node instanceof AST_ObjectKeyVal) {
104 add(node.key, ignore_quoted && node.quote);
106 else if (node instanceof AST_ObjectProperty) {
107 // setter or getter, since KeyVal is handled above
110 else if (node instanceof AST_Dot) {
113 else if (node instanceof AST_Sub) {
114 addStrings(node.property, ignore_quoted);
118 // step 2: transform the tree, renaming properties
119 return ast.transform(new TreeTransformer(function(node){
120 if (node instanceof AST_ObjectKeyVal) {
121 if (!(ignore_quoted && node.quote))
122 node.key = mangle(node.key);
124 else if (node instanceof AST_ObjectProperty) {
126 node.key.name = mangle(node.key.name);
128 else if (node instanceof AST_Dot) {
129 node.property = mangle(node.property);
131 else if (node instanceof AST_Sub) {
133 node.property = mangleStrings(node.property);
135 // else if (node instanceof AST_String) {
136 // if (should_mangle(node.value)) {
138 // "Found \"{prop}\" property candidate for mangling in an arbitrary string [{file}:{line},{col}]", {
139 // file : node.start.file,
140 // line : node.start.line,
141 // col : node.start.col,
149 // only function declarations after this line
151 function can_mangle(name) {
152 if (!is_identifier(name)) return false;
153 if (unmangleable.indexOf(name) >= 0) return false;
154 if (reserved.indexOf(name) >= 0) return false;
155 if (options.only_cache) {
156 return cache.props.has(name);
158 if (/^[0-9.]+$/.test(name)) return false;
162 function should_mangle(name) {
163 if (ignore_quoted && name in ignored) return false;
164 if (regex && !regex.test(name)) return false;
165 if (reserved.indexOf(name) >= 0) return false;
166 return cache.props.has(name)
167 || names_to_mangle.indexOf(name) >= 0;
170 function add(name, ignore) {
172 ignored[name] = true;
176 if (can_mangle(name))
177 push_uniq(names_to_mangle, name);
179 if (!should_mangle(name)) {
180 push_uniq(unmangleable, name);
184 function mangle(name) {
185 if (!should_mangle(name)) {
189 var mangled = cache.props.get(name);
192 // debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
193 var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
195 if (can_mangle(debug_mangled) && !(ignore_quoted && debug_mangled in ignored)) {
196 mangled = debug_mangled;
200 // either debug mode is off, or it is on and we could not use the mangled name
202 // note can_mangle() does not check if the name collides with the 'ignored' set
203 // (filled with quoted properties when ignore_quoted set). Make sure we add this
204 // check so we don't collide with a quoted name.
206 mangled = base54(++cache.cname);
207 } while (!can_mangle(mangled) || (ignore_quoted && mangled in ignored));
210 cache.props.set(name, mangled);
215 function addStrings(node, ignore) {
218 (function walk(node){
219 node.walk(new TreeWalker(function(node){
220 if (node instanceof AST_Seq) {
224 if (node instanceof AST_String) {
225 add(node.value, ignore);
228 if (node instanceof AST_Conditional) {
229 walk(node.consequent);
230 walk(node.alternative);
237 if (ex !== out) throw ex;
241 function mangleStrings(node) {
242 return node.transform(new TreeTransformer(function(node){
243 if (node instanceof AST_Seq) {
244 node.cdr = mangleStrings(node.cdr);
246 else if (node instanceof AST_String) {
247 node.value = mangle(node.value);
249 else if (node instanceof AST_Conditional) {
250 node.consequent = mangleStrings(node.consequent);
251 node.alternative = mangleStrings(node.alternative);